Menu

QUTest on POSIX target

Eddie_1066
2024-07-31
2024-08-29
  • Eddie_1066

    Eddie_1066 - 2024-07-31

    Hi Miro.
    I'm trying to build and run the qutest example for posix on a Beaglebone black running
    Linux 6.1.46-g1d4b5da681 #1 PREEMPT Thu Oct 19 10:19:08 UTC 2023 armv7l armv7l armv7l GNU/Linux
    and qp 7.3.3

    when I run make as below I get TARGET ERROR infinit loop
    root@bbb-m1914:~/qp/qpcpp/examples/qutest/dpp/test_dpp# make -f make_posix HOST=192.168.0.110:6601
    #set -e; while true; do \
    echo "restarting build_posix/test_dpp"; \
    build_posix/test_dpp "" 192.168.0.110:6601; \
    done
    restarting build_posix/test_dpp
    <target> ERROR cannot connect to QSPY at host=localhost:6601
    <target> ERROR invalid TCP socket
    restarting build_posix/test_dpp
    <target> ERROR cannot connect to QSPY at host=localhost:6601
    <target> ERROR invalid TCP socket
    restarting build_posix/test_dpp
    <target> ERROR cannot connect to QSPY at host=localhost:6601
    <target> ERROR invalid TCP socket
    restarting build_posix/test_dpp
    <target> ERROR cannot connect to QSPY at host=localhost:6601
    <target> ERROR invalid TCP socket
    restarting build_posix/test_dpp
    <target> ERROR cannot connect to QSPY at host=localhost:6601
    <target> ERROR invalid TCP socket</target></target></target></target></target></target></target></target></target></target>

    make show
    root@bbb-m1914:~/qp/qpcpp/examples/qutest/dpp/test_dpp# make show -f make_posix HOST=192.168.0.110:6601 PROJECT = test_dpp TARGET_EXE = build_posix/test_dpp VPATH = . ../src ../../../../src/qf ../../../../src/qs ../../../../ports/posix-qutest C_SRCS = CPP_SRCS = bsp.cpp philo.cpp table.cpp test_dpp.cpp qep_hsm.cpp qep_msm.cpp qf_act.cpp qf_actq.cpp qf_defer.cpp qf_dyn.cpp qf_mem.cpp qf_ps.cpp qf_qact.cpp qf_qeq.cpp qf_qmact.cpp qf_time.cpp qs.cpp qs_64bit.cpp qs_rx.cpp qs_fp.cpp qutest.cpp qutest_port.cpp C_DEPS_EXT = C_OBJS_EXT = C_DEPS_EXT = CPP_DEPS_EXT = build_posix/bsp.d build_posix/philo.d build_posix/table.d build_posix/test_dpp.d build_posix/qep_hsm.d build_posix/qep_msm.d build_posix/qf_act.d build_posix/qf_actq.d build_posix/qf_defer.d build_posix/qf_dyn.d build_posix/qf_mem.d build_posix/qf_ps.d build_posix/qf_qact.d build_posix/qf_qeq.d build_posix/qf_qmact.d build_posix/qf_time.d build_posix/qs.d build_posix/qs_64bit.d build_posix/qs_rx.d build_posix/qs_fp.d build_posix/qutest.d build_posix/qutest_port.d CPP_OBJS_EXT = build_posix/bsp.o build_posix/philo.o build_posix/table.o build_posix/test_dpp.o build_posix/qep_hsm.o build_posix/qep_msm.o build_posix/qf_act.o build_posix/qf_actq.o build_posix/qf_defer.o build_posix/qf_dyn.o build_posix/qf_mem.o build_posix/qf_ps.o build_posix/qf_qact.o build_posix/qf_qeq.o build_posix/qf_qmact.o build_posix/qf_time.o build_posix/qs.o build_posix/qs_64bit.o build_posix/qs_rx.o build_posix/qs_fp.o build_posix/qutest.o build_posix/qutest_port.o LIB_DIRS = LIBS = -lpthread DEFINES = -DQP_API_VERSION=9999 HOST = 192.168.0.110:6601

    Am'I missing something here?
    
    Eddie
    
     
  • Quantum Leaps

    Quantum Leaps - 2024-07-31

    Hi Eddie,
    Have you watched the video?

     
  • Eddie_1066

    Eddie_1066 - 2024-07-31

    Yes, I used it as a guide.

     
  • Eddie_1066

    Eddie_1066 - 2024-07-31

    to run the test I'm using the instructions provided in the make make_posix file
    make -f make_posix HOST=192.168.1.65:6601

    # examples of invoking this Makefile:
    # make -f make_posix        # make and run the tests in the current directory
    # make -f make_posix HOST=192.168.1.65:6601 # make and run the executable
    # make -f make_posix norun   # only make but not run the tests
    # make -f make_posix clean   # cleanup the build
    

    From the make print out it looks like HOST=192.168.1.65:6601 is being ignored as qutest tries to connect to localhost

    restarting build_posix/test_dpp
    <target> ERROR cannot connect to QSPY at host=localhost:6601
    <target> ERROR invalid TCP socket
    
     
  • Quantum Leaps

    Quantum Leaps - 2024-07-31

    Hi Eddie,
    The QUTest DPP examples have "lost" the ability to pass command-line arguments from invocation of the test fixture. Specifically, the main() in the fixtures didn't take or pass the usual argc, argv parameters to QS_init(). This worked fine when target and host are identical, but obviously stopped working when they are different, as is the case of the POSIX tests.

    So, the problem was:

    int main() {
        QF_init();  // initialize the framework and the underlying RT kernel
        BSP_init(); // initialize the Board Support Package
    

    But it should be:

    int main(int argc, char* argv[]) {
        QF_init(); // initialize the framework and the underlying RT kernel
        BSP_init(argc, argv); // initialize the Board Support Package
    

    Anyway, the qpc-examples and qpcpp-examples repos have been updated. Please try the new DPP version.

    I apologize for this omission. QUTest is so flexible that it's sometimes difficult to keep up with all the possible targets and use cases...

    --MMS

     
  • Eddie_1066

    Eddie_1066 - 2024-08-01

    Hi Miro
    Thanks for the fast response.
    I tried the new update and got this out put

    _port.o build_posix/qstamp.o  -lpthread
    set -e; while true; do \
            echo "restarting build_posix/test_dpp"; \
            build_posix/test_dpp "" 192.168.0.7:6601; \
    done
    restarting build_posix/test_dpp
    <TARGET> ERROR   cannot resolve host Name=:6601,Err=-2
    <TARGET> ERROR   invalid TCP socket
    restarting build_posix/test_dpp
    <TARGET> ERROR   cannot resolve host Name=:6601,Err=-2
    <TARGET> ERROR   invalid TCP socket
    

    which is not correct.
    this line pointed me to issue
    build_posix/test_dpp "" 192.168.0.7:6601;

    when the ip and port number are passed to test_dpp there is an extra "" which is not present in your video example. Lookin in the make_posix file.

    # run the test fixture on a POSIX target in a loop, so that it is re-started
    # after every test. The loop is terminated by pressing Ctrl-C on the keyboard.
    #
    run : $(TARGET_EXE)
        set -e; while true; do \
            echo "restarting $(TARGET_EXE)"; \
            $(TARGET_EXE) "" $(HOST); \
        done
    

    there is quotations between the exe and host variables, removing the quotations fixes the error
    $(TARGET_EXE) "" $(HOST);
    Now I'm getting the expected output.

    On another topic, I have been developing my application using qutest and TDD. I'm at the point now were I would like to setup integration testing on the target. Is the dpp example the best example to use as a guide or C:\qp\qpcpp\test as in this post
    https://sourceforge.net/p/qpc/discussion/668726/thread/8df83c766c/?limit=25#243d

    Eddie

     

    Last edit: Eddie_1066 2024-08-01
  • Quantum Leaps

    Quantum Leaps - 2024-08-01

    Hi Eddie,
    OK, I've fixed the reported issues in the make_posix files. The updates have been checked into the qpc-examples and qpcpp-examples repos on GitHub.

    I'm at the point now were I would like to setup integration testing on the target...

    Yes, QUTest can be used for integration tests as well. In that case, you typically run the whole application on top of the real-time kernel of your choice (e.g., QK or a 3rd-party RTOS, or POSIX). This is in contrast to unit tests, where you test isolated Active Objects or small groups of Active Objects. The test fixtures for such unit tests link with the "QP-stub" that contans a simple QUTest event loop, which you could consider as a "QUTest kernel".

    QUTest has a special command-line configuration for integration tests: Q_UTEST 0, please read the Q_UTEST documentation.

    The only limitation is that the target should NOT produce any QSpy output "by itself" because the test scripts would not be able to "expect" such an output. In other words, as usual in QUTest, all QSpy output must be initiated by the test script, so that the test script knows what and when to "expect" such output. I hope this makes sense to you.

    Integration tests with Q_UTEST 0 are demonstrated in the <qpc|qpcpp>/test/ directory. You can find there integration tests that utilize the QK and QXK kernels. Please review the code for these tests. When you do, you'll see that the applications intentioanlly do NOT start the clock tick ISR. This is precisely to avoid any output that QUTest scripts wouldn't be able to "expect".

    --MMS

     

    Last edit: Quantum Leaps 2024-08-01
  • Eddie_1066

    Eddie_1066 - 2024-08-25

    Hi MMS

    The <qpc|qpcpp>/test/ example does not have a posix makefile to show how to build for posix. Thus, I have been trying to build /qp/qpcpp/examples/qutest/dpp/test_dpp with -DQ_UTEST=0
    ld flag. The build keep failing, I have tried both:
    QP_PORT_DIR := $(QPCPP)/ports/posix-qutest
    and
    QP_PORT_DIR := $(QPCPP)/ports/posix
    ports. with the error below</qpc|qpcpp>

    When liking with posix-qutest
    QP_PORT_DIR := $(QPCPP)/ports/posix-qutest

    .o
    <command-line>: warning: "Q_UTEST" redefined
    <command-line>: note: this is the location of the previous definition
    g++ -c -g -O -Wall -W -fno-rtti -fno-exceptions -I. -I../src -I../../../../include -I../../../../ports/posix-qutest -DQP_API_VERSION=9999 -DQ_SPY -DQ_UTEST -DQ_HOST -DQ_UTEST=0 ../../../../src/qs/qstamp.cpp -o build_posix/qstamp.o
    <command-line>: warning: "Q_UTEST" redefined
    <command-line>: note: this is the location of the previous definition
    g++ -no-pie -o build_posix/test_dpp build_posix/bsp.o build_posix/philo.o build_posix/table.o build_posix/test_dpp.o build_posix/qep_hsm.o build_posix/qep_msm.o build_posix/qf_act.o build_posix/qf_actq.o build_posix/qf_defer.o build_posix/qf_dyn.o build_posix/qf_mem.o build_posix/qf_ps.o build_posix/qf_qact.o build_posix/qf_qeq.o build_posix/qf_qmact.o build_posix/qf_time.o build_posix/qs.o build_posix/qs_64bit.o build_posix/qs_rx.o build_posix/qs_fp.o build_posix/qutest.o build_posix/qutest_port.o build_posix/qstamp.o -lpthread
    /usr/lib/gcc/arm-oe-linux-gnueabi/11.4.0/../../../../arm-oe-linux-gnueabi/bin/ld: build_posix/test_dpp.o: in function main': /home/root/qp/qpcpp/examples/qutest/dpp/test_dpp/test_dpp.cpp:40: undefined reference toQP::QF::init()'
    /usr/lib/gcc/arm-oe-linux-gnueabi/11.4.0/../../../../arm-oe-linux-gnueabi/bin/ld: build_posix/test_dpp.o: in function QP::QActive::start(unsigned short, QP::QEvt const**, unsigned int, void*, unsigned int)': /home/root/qp/qpcpp/examples/qutest/dpp/test_dpp/../../../../include/qp.hpp:817: undefined reference toQP::QActive::start(unsigned short, QP::QEvt const, unsigned int, void, unsigned int, void const)'
    /usr/lib/gcc/arm-oe-linux-gnueabi/11.4.0/../../../../arm-oe-linux-gnueabi/bin/ld: /home/root/qp/qpcpp/examples/qutest/dpp/test_dpp/../../../../include/qp.hpp:817: undefined reference to QP::QActive::start(unsigned short, QP::QEvt const**, unsigned int, void*, unsigned int, void const*)' /usr/lib/gcc/arm-oe-linux-gnueabi/11.4.0/../../../../arm-oe-linux-gnueabi/bin/ld: build_posix/test_dpp.o: in functionmain':
    /home/root/qp/qpcpp/examples/qutest/dpp/test_dpp/test_dpp.cpp:73: undefined reference to QP::QF::run()' /usr/lib/gcc/arm-oe-linux-gnueabi/11.4.0/../../../../arm-oe-linux-gnueabi/bin/ld: build_posix/qs_rx.o: in functionQP::QS::rxParse()':
    /home/root/qp/qpcpp/examples/qutest/dpp/test_dpp/../../../../src/qs/qs_rx.cpp:775: undefined reference to QP::QTimeEvt::tick1_(unsigned char, void const*)' /usr/lib/gcc/arm-oe-linux-gnueabi/11.4.0/../../../../arm-oe-linux-gnueabi/bin/ld: build_posix/qutest_port.o: in functionQP::QS::onFlush()':
    /home/root/qp/qpcpp/examples/qutest/dpp/test_dpp/../../../../ports/posix-qutest/qutest_port.cpp:218: undefined reference to QP::QF::stop()' /usr/lib/gcc/arm-oe-linux-gnueabi/11.4.0/../../../../arm-oe-linux-gnueabi/bin/ld: /home/root/qp/qpcpp/examples/qutest/dpp/test_dpp/../../../../ports/posix-qutest/qutest_port.cpp:236: undefined reference toQP::QF::stop()'
    /usr/lib/gcc/arm-oe-linux-gnueabi/11.4.0/../../../../arm-oe-linux-gnueabi/bin/ld: build_posix/qutest_port.o: in function QP::QS::onStartup(void const*)': /home/root/qp/qpcpp/examples/qutest/dpp/test_dpp/../../../../ports/posix-qutest/qutest_port.cpp:171: undefined reference toQP::QF::stop()'
    /usr/lib/gcc/arm-oe-linux-gnueabi/11.4.0/../../../../arm-oe-linux-gnueabi/bin/ld: build_posix/qutest_port.o: in function QP::QS::onTestLoop()': /home/root/qp/qpcpp/examples/qutest/dpp/test_dpp/../../../../ports/posix-qutest/qutest_port.cpp:275: undefined reference toQP::QF::stop()'
    collect2: error: ld returned 1 exit status
    make: *** [make_posix:195: build_posix/test_dpp] Error 1</command-line></command-line></command-line></command-line>

    linking with just posix port
    QP_PORT_DIR := $(QPCPP)/ports/posix

    <command-line>: warning: "Q_UTEST" redefined
    <command-line>: note: this is the location of the previous definition
    g++ -c -g -O -Wall -W -fno-rtti -fno-exceptions -I. -I../src -I../../../../include -I../../../../ports/posix -DQP_API_VERSION=9999 -DQ_SPY -DQ_UTEST -DQ_HOST -DQ_UTEST=0 ../../../../src/qs/qs_rx.cpp -o build_posix/qs_rx.o
    <command-line>: warning: "Q_UTEST" redefined
    <command-line>: note: this is the location of the previous definition
    g++ -c -g -O -Wall -W -fno-rtti -fno-exceptions -I. -I../src -I../../../../include -I../../../../ports/posix -DQP_API_VERSION=9999 -DQ_SPY -DQ_UTEST -DQ_HOST -DQ_UTEST=0 ../../../../src/qs/qs_fp.cpp -o build_posix/qs_fp.o
    <command-line>: warning: "Q_UTEST" redefined
    <command-line>: note: this is the location of the previous definition
    g++ -c -g -O -Wall -W -fno-rtti -fno-exceptions -I. -I../src -I../../../../include -I../../../../ports/posix -DQP_API_VERSION=9999 -DQ_SPY -DQ_UTEST -DQ_HOST -DQ_UTEST=0 ../../../../src/qs/qutest.cpp -o build_posix/qutest.o
    <command-line>: warning: "Q_UTEST" redefined
    <command-line>: note: this is the location of the previous definition
    make: *** No rule to make target 'build_posix/qutest_port.o', needed by 'build_posix/test_dpp'. Stop.
    root@bbb-m1914:~/qp/qpcpp/examples/qutest/dpp/test_dpp#</command-line></command-line></command-line></command-line></command-line></command-line></command-line></command-line>

    In the make file I added the flag as
    

    ~~~
    CFLAGS = -c -g -O -Wall -Wstrict-prototypes -W $(INCLUDES) $(DEFINES) \
    -DQ_SPY -DQ_UTEST -DQ_HOST -DQ_UTEST=0

    CPPFLAGS = -c -g -O -Wall -W -fno-rtti -fno-exceptions $(INCLUDES) $(DEFINES) \
    -DQ_SPY -DQ_UTEST -DQ_HOST -DQ_UTEST=0
    ~~~

    I'm not sure what I'm missing here, how do I compile for a posix target with Q_UTEST=0
    
     
  • Quantum Leaps

    Quantum Leaps - 2024-08-28

    Hi Eddie,

    The <qpc|qpcpp>/test/ example does not have a posix makefile to show how to build for posix</qpc|qpcpp>

    It seems to me that you're getting confused about the official QP releases and the immediate chantges on GitHub. Of couse, at this point the changes are NOT in the official release yet, so they are NOT in the directory <qpc|qpcpp>/test/examples.

    In my previous post I referred to the immediate changes on GitHub. These changes are located in the qpcpp-examples GitHub repo, which is a sub-module of the qpcpp GitHub repo. I specifically include the links in my posts, so please click on those links to see what they refer to.

    Anyway, the makefiles you need are here:
    - qpcpp-examples/qutest/dpp/test_dpp/make_posix
    - qpcpp-examples/qutest/dpp-comp/test_dpp/make_posix
    - qpcpp-examples/qutest/self_test/test/make_posix

    If you want any of these makefiles, please click on the "Download raw file" arrow at the top of the GitHub view. No need to reinvent the wheel.

    Of course, all the updates to the qpcpp-examples will be included in the upcoming official QP release.

    Please post to this forum how the makefiles from GitHub work for you.

    --MMS

     
  • Eddie_1066

    Eddie_1066 - 2024-08-29

    Hi MMS,
    Using the repo you linked above, the qpcpp-examples/qutest/self_test/test example builds fine with -DQ_UTEST flag in the make_posix file. However with -DQ_UTEST=0 I get this error:

    /home/root/qp/qpcpp/examples/qutest/self_test/test/../../../../ports/posix-qutest/qutest_port.cpp:171: undefined reference to `QP::QF::stop()'
    /usr/lib/gcc/arm-oe-linux-gnueabi/11.4.0/../../../../arm-oe-linux-gnueabi/bin/ld: build_posix/qutest_port.o: in function `QP::QS::onTestLoop()':
    /home/root/qp/qpcpp/examples/qutest/self_test/test/../../../../ports/posix-qutest/qutest_port.cpp:275: undefined reference to `QP::QF::stop()'
    collect2: error: ld returned 1 exit status
    make: *** [make_posix:192: build_posix/test_qutest] Error 1
    root@bbb-m1914:~/qp/qpcpp/examples/qutest/self_test/test#
    

    for the test_dpp example I get the same error as before, with -DQ_UTEST=0 flag
    -DQ_UTEST works as expected.

    g++    -no-pie  -o build_posix/test_dpp build_posix/bsp.o build_posix/philo.o build_posix/table.o build_posix/test_dpp.o build_posix/qep_hsm.o build_posix/qep_msm.o build_posix/qf_act.o build_posix/qf_actq.o build_posix/qf_defer.o build_posix/qf_dyn.o build_posix/qf_mem.o build_posix/qf_ps.o build_posix/qf_qact.o build_posix/qf_qeq.o build_posix/qf_qmact.o build_posix/qf_time.o build_posix/qs.o build_posix/qs_64bit.o build_posix/qs_rx.o build_posix/qs_fp.o build_posix/qutest.o build_posix/qutest_port.o build_posix/qstamp.o -lpthread
    /usr/lib/gcc/arm-oe-linux-gnueabi/11.4.0/../../../../arm-oe-linux-gnueabi/bin/ld: build_posix/test_dpp.o: in function `main':
    /home/root/qp/qpcpp/examples/qutest/dpp/test_dpp/test_dpp.cpp:40: undefined reference to `QP::QF::init()'
    /usr/lib/gcc/arm-oe-linux-gnueabi/11.4.0/../../../../arm-oe-linux-gnueabi/bin/ld: build_posix/test_dpp.o: in function `QP::QActive::start(unsigned short, QP::QEvt const**, unsigned int, void*, unsigned int)':
    /home/root/qp/qpcpp/examples/qutest/dpp/test_dpp/../../../../include/qp.hpp:814: undefined reference to `QP::QActive::start(unsigned short, QP::QEvt const**, unsigned int, void*, unsigned int, void const*)'
    /usr/lib/gcc/arm-oe-linux-gnueabi/11.4.0/../../../../arm-oe-linux-gnueabi/bin/ld: /home/root/qp/qpcpp/examples/qutest/dpp/test_dpp/../../../../include/qp.hpp:814: undefined reference to `QP::QActive::start(unsigned short, QP::QEvt const**, unsigned int, void*, unsigned int, void const*)'
    /usr/lib/gcc/arm-oe-linux-gnueabi/11.4.0/../../../../arm-oe-linux-gnueabi/bin/ld: build_posix/test_dpp.o: in function `main':
    /home/root/qp/qpcpp/examples/qutest/dpp/test_dpp/test_dpp.cpp:73: undefined reference to `QP::QF::run()'
    /usr/lib/gcc/arm-oe-linux-gnueabi/11.4.0/../../../../arm-oe-linux-gnueabi/bin/ld: build_posix/qs_rx.o: in function `QP::QS::rxParse()':
    /home/root/qp/qpcpp/examples/qutest/dpp/test_dpp/../../../../src/qs/qs_rx.cpp:775: undefined reference to `QP::QTimeEvt::tick1_(unsigned char, void const*)'
    /usr/lib/gcc/arm-oe-linux-gnueabi/11.4.0/../../../../arm-oe-linux-gnueabi/bin/ld: build_posix/qutest_port.o: in function `QP::QS::onFlush()':
    /home/root/qp/qpcpp/examples/qutest/dpp/test_dpp/../../../../ports/posix-qutest/qutest_port.cpp:218: undefined reference to `QP::QF::stop()'
    /usr/lib/gcc/arm-oe-linux-gnueabi/11.4.0/../../../../arm-oe-linux-gnueabi/bin/ld: /home/root/qp/qpcpp/examples/qutest/dpp/test_dpp/../../../../ports/posix-qutest/qutest_port.cpp:236: undefined reference to `QP::QF::stop()'
    /usr/lib/gcc/arm-oe-linux-gnueabi/11.4.0/../../../../arm-oe-linux-gnueabi/bin/ld: build_posix/qutest_port.o: in function `QP::QS::onStartup(void const*)':
    /home/root/qp/qpcpp/examples/qutest/dpp/test_dpp/../../../../ports/posix-qutest/qutest_port.cpp:171: undefined reference to `QP::QF::stop()'
    /usr/lib/gcc/arm-oe-linux-gnueabi/11.4.0/../../../../arm-oe-linux-gnueabi/bin/ld: build_posix/qutest_port.o: in function `QP::QS::onTestLoop()':
    /home/root/qp/qpcpp/examples/qutest/dpp/test_dpp/../../../../ports/posix-qutest/qutest_port.cpp:275: undefined reference to `QP::QF::stop()'
    collect2: error: ld returned 1 exit status
    make: *** [make_posix:195: build_posix/test_dpp] Error 1
    

    what is the difference/advantage when using the the full qp kernel over qutest stab for intergration testing?

     
  • Quantum Leaps

    Quantum Leaps - 2024-08-29

    Hi Eddie,

    what is the difference/advantage when using the the full qp kernel over qutest stab for intergration testing?

    In unit tests, you typically don't use the complete QP framework (or complete RTOS, if you were to use a traditional RTOS instead of QP). At this lowest level of testing you simply don't have (and you don't want) a full application. You want to have just enough infrastructure to test one Active Object in isolation, both on the target and on the host. For this you simply cannot use a specific kernel/RTOS/OS because most of them won't run on the host. This is where the QP-stub conveniently provided by QUTest comes in. You can think of this QP-stub as the test-double for the real QP framework.

    However, in integration testing you want to test the complete (or almost complete) application. Such integration tests then run only on the target and don't run on the host anymore. So, in this case, you specifically don't want any QP-stubs. You want the real thing.

    So now, going back to your linking errors, they show that you miss the whole QP port (to POSIX in this case). In other words, your build must be almost identical to the real application build for the target. Please refer to the examples of integration tests in the qpcpp/test directory.

    --MMS

     

    Last edit: Quantum Leaps 2024-08-30

Log in to post a comment.