Skip to content

Commit

Permalink
Ran a markdown LINTer over the documentation.
Browse files Browse the repository at this point in the history
  • Loading branch information
sagatowski committed Nov 17, 2023
1 parent 3ca07c5 commit bb35db2
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 71 deletions.
62 changes: 45 additions & 17 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ title: F.A.Q.
---

## Frequently asked questions

Here you’ll find the most commonly asked questions and their answers.
If you don’t find what you are looking for here, you can look through the:

Expand All @@ -27,8 +28,8 @@ If you don’t find what you are looking for here, you can look through the:

---


### 1. How can I run a test across multiple PLC cycles?

This can be accomplished by keeping the function block under test as an instance variable of the test suite rather than the test method.
You can download an [example here](https://tcunit.org/temp/TimedTest_1x.zip).
In this example, the `FB_ToBeTested` is instantiated under the test suite (`FB_ToBeTested_Test`), and can thus be controlled over multiple cycles.
Expand All @@ -37,9 +38,10 @@ Then all that’s necessary to do is to set the condition for when the assertion
**Required TcUnit version:** 1.0 or later

### 2. How can I disable/ignore a test?

Add `DISABLED_` in front of the test name, for example:

```
```StructuredText
TEST('DISABLED_ThisTestWillBeIgnored');
AssertEquals(Expected := a,
Expand All @@ -48,9 +50,11 @@ AssertEquals(Expected := a,
TEST_FINISHED();
```

**Required TcUnit version:** 1.0 or later

### 3. Is there a way to test %I* or %Q* variables?
### 3. Is there a way to test `%I*` or `%Q*` variables?

In a number of scenarios, TwinCAT won't let you write directly to certain variables:

- Due to access restrictions (e.g. a variable in a FB's VAR)
Expand All @@ -59,29 +63,38 @@ In a number of scenarios, TwinCAT won't let you write directly to certain variab
Writing to these variables wouldn’t make sense and should be prevented in the normal PLC code, so having special privileges during testing is a must.
To support these cases, TcUnit provides helper functions like `WRITE_PROTECTED_BOOL()`, `WRITE_PROTECTED_INT()` (and so forth) for setting these type of variables.
For an example of how to use these, let's assume you have a test:
```

```StructuredText
METHOD PRIVATE TestCommsOkChannelsLow
VAR
EL1008 : FB_Beckhoff_EL1008;
END_VAR
```

Where the `FB_Beckhoff_EL1008` holds a variable:
```

```StructuredText
iChannelInput AT %I* : ARRAY[1..8] OF BOOL;
```

Now you might want to write a value to the first channel of the iChannelInput like:
```

```StructuredText
TcUnit.WRITE_PROTECTED_BOOL(Ptr := ADR(EL1008.iChannelInput[1]),
Value := FALSE);
```

Whereas afterwards you can make an assertion as usual:
```

```StructuredText
AssertFalse(Condition := EL1008.ChannelInput[1],
Message := 'Channel is not false');
```

**Required TcUnit version:** 1.0 or later

### 4. Is there a way to hide TcUnit in my libraries?

You can accomplish this by the [Hide reference](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/18014402725266443.html&id=) option for referenced libraries.
This option lets you hide TcUnit from your other projects.
Let’s assume you’ve developed a library `MyLibrary`, which has tests written in TcUnit.
Expand All @@ -92,17 +105,17 @@ You can find it in the Properties tab:

![Hide reference](img/hide-reference.png)


**Required TcUnit version:** 1.0 or later

### 5. How do I do assertions on the BIT datatype?

I want to do an assertion on two variables both declared with the `BIT`-datatype, but I have noticed that a `AssertEquals_BIT()` does not exist.
What do I do?

The reason a `AssertEquals_BIT()` does not exist is that TwinCAT does not allow a BIT-datatype as a variable input.
If you have data declared with the BIT-type, the easiest way to do an assertion on these is to do a `BIT_TO_BOOL()` conversion and use the `AssertEquals_BOOL()`.

```
```StructuredText
TEST('Testing_of_BIT_Type');
AssertEquals_BOOL(Expected := BIT_TO_BOO(VariableDeclaredAsBit_A),
Expand All @@ -115,6 +128,7 @@ TEST_FINISHED();
**Required TcUnit version:** 1.0 or later

### 6. When I run more than 100 tests in a single test-suite I get the wrong results, why?

When TcUnit is running it allocates memory in the PLC to store the test results.
The maximum number of tests for every test suite has been set to 100, which however is a configuration parameter for TcUnit and can be changed.
Parameters for TcUnit (and in fact any library references) are stored in your project, which means that this change will be persistent for your project/library.
Expand All @@ -124,8 +138,8 @@ To change this max amount, to say for instance 200 tests per test suite, go to t

**Required TcUnit version:** 1.0 or later


### 7. Is it possible to run test suites and/or tests in a sequence?

Yes.
By default TcUnit runs all the test suites and tests in parallel, in other words all test suites and tests are run at the same time.
Sometimes it is however desirable to run either the test suites or tests (or both) in a sequence, for example if you get exceed overruns while running tests.
Expand All @@ -134,7 +148,8 @@ Since TcUnit 1.2 it's possible to run test suites in sequence (one after another
To execute test suites in a sequence, simply replace `TcUnit.RUN()` with `TcUnit.RUN_IN_SEQUENCE()` in your main body of the test program.
This will execute the test suites in the order that they were declared.
So for example if we have defined the following test suites and test program:
```

```StructuredText
PROGRAM PRG_TEST
VAR
fbDiagnosticMessageDiagnosticCodeParser_Test : FB_DiagnosticMessageDiagnosticCodeParser_Test;
Expand All @@ -143,14 +158,16 @@ VAR
END_VAR
-------------------------
TcUnit.RUN_IN_SEQUENCE();
```

This will first execute all tests defined in `fbDiagnosticMessageDiagnosticCodeParser_Test`, once all tests are finished in that function block, TcUnit will execute all tests in `fbDiagnosticMessageFlagsParser_Test`, and when that is done it will execute all tests in `fbDiagnosticMessageParser_Test`.

It's also possible to execute individual tests in order by simply replacing `TEST('TestName')` with `TEST_ORDERED('TestName')`.
This will execute the tests in the order that the `TEST_ORDERED()` is called for the various tests.
`TEST_ORDERED()` returns a boolean to indicate whether the TcUnit framework will run the test, so in order to only execute the code when it's time for that particular test, it makes sense to check if `TEST_ORDERED()` returns true, and only then do the execution of the function blocks and assertions, for example like this:

```
```StructuredText
METHOD PRIVATE TestWithTimestampZeroTimeExpectCurrentTime
VAR
... (variable declaration used for the test)
Expand All @@ -166,14 +183,14 @@ IF TEST_ORDERED('TestWithTimestampZeroTimeExpectCurrentTime') THEN
TEST_FINISHED();
END_IF
```

As usual, the `TEST_FINISHED()` will indicate that this test is finished, and the framework will go to the next test.
Note that you don't need to create any state machine for calling the different `TEST_ORDERED()` tests.
You can (and must!) call all `TEST_ORDERED()` at the same time.
The framework will make sure to only care about the assertions of the test that is currently running.

This means the following combinations can be used:


- `RUN()` with all tests as `TEST()` – means all tests suites and tests will run in parallel, this is the default behaviour.
![TcUnit run option 1](img/tcunit_run_option1.png)
- `RUN_IN_SEQUENCE()` with all tests as `TEST()` – means all test suites will run in sequence, but the tests in every test suite will run in parallel.
Expand All @@ -197,6 +214,7 @@ For a couple of TwinCAT projects that shows how to run both test suites in a seq
**Required TcUnit version:** 1.2 or later

### 8. Why is it taking so long to get the results from TcUnit?

If you have many test suites and/or tests, it can take some time for TcUnit to print all those results.
Since version 1.1 of TcUnit, much more data is printed to the ADS-logger as this data is used for the communication with TcUnit-Runner.
If you know that you will only run your tests locally and without integration to a CI/CD tool using TcUnit-Runner, you can set the parameter `LogExtendedResults` to `FALSE` (it is default `TRUE`).
Expand All @@ -207,6 +225,7 @@ To change this parameter, go to the library references and select TcUnit, then g
**Required TcUnit version:** 1.1 or later

### 9. Is it possible to have a time delay between the execution of the test suites?

Yes.
You can set the parameter `TimeBetweenTestSuitesExecution` to whatever delay you want to have.
To change this parameter, go to the library references and select TcUnit, then go to `GVLs``GVL_Param_TcUnit``TimeBetweenTestSuitesExecution`.
Expand All @@ -218,9 +237,11 @@ For example, in the below screenshot this is changed to 5 seconds.
**Required TcUnit version:** 1.2 or later

### 10. If I call ADSLOGSTR(), my messages don't show up in the correct sequence. Why?

If I call `Tc2_System.ADSLOGSTR()` during execution of a test, my messages don't arrive in the expected order.
Let's for example assume this very simple (always failing) test:
```

```StructuredText
TEST('Test1');
FOR nCounter := 1 TO 5 BY 1 DO
Tc2_System.ADSLOGSTR(msgCtrlMask := ADSLOG_MSGTYPE_HINT,
Expand Down Expand Up @@ -255,9 +276,11 @@ So if we replaced the call to `Tc2_System.ADSLOGSTR()` to `TCUNIT_ADSLOGSTR()` i
**Required TcUnit version:** 1.2 or later

### 11. How do I test functions?

It's done almost identical as in the introduction user guide, but simply replace the instance of the function block that you want to test with the call to the function instead.
Assume we have a function:
```

```StructuredText
FUNCTION F_Sum
VAR_INPUT
one : UINT;
Expand All @@ -266,8 +289,10 @@ END_VAR
F_Sum := one + two;
```

Then the test would look like following:
```

```StructuredText
METHOD TwoPlusTwoEqualsFour
VAR
Result : UINT;
Expand All @@ -283,11 +308,14 @@ AssertEquals(Expected := ExpectedSum,
Message := 'The calculation is not correct');
TEST_FINISHED();
```

**Required TcUnit version:** 1.0 or later

### 12. I have problems running TcUnit on a ARMv7 controller, why?

When running TcUnit with a controller using ARMv7 you can run into issues, such as breakpoints not working.
This seems to be an issue with the limited memory of the controllers using an ARMv7 such as the CX8190 and CX9020. Please adjust the [parameters related to memory allocation](#6-when-i-run-more-than-100-tests-in-a-single-test-suite-i-get-the-wrong-results-why).

For more information on a set of working parameters, see [this issue on GitHub](https://github.com/tcunit/TcUnit/issues/148).
For more information on a set of working parameters, see [this issue on GitHub](https://github.com/tcunit/TcUnit/issues/148).
Binary file added docs/img/TcUnit16Of17Failed_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/TcUnitManyFails.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 7 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ layout: home
list_title: ' '
---

# TcUnit

![TcUnit logo](img/tcunit-logo.png)

TcUnit is an [xUnit](https://en.wikipedia.org/wiki/XUnit) type of framework specifically done for [Beckhoff’s TwinCAT 3](https://www.beckhoff.com/en-en/products/automation/twincat/) development environment.
Expand All @@ -17,18 +19,22 @@ Start by reading the [unit testing concepts](unit-testing-concepts.md) and then
![TcUnit introduction](img/tcunit-general.png)

## Easy to use

The framework is easy to use.
All that is needed is to download & install the library, and provide a reference to the TcUnit-library in your project, and you can start to write your test code.
For a complete set of instructions, start with [the concepts](unit-testing-concepts.md), continue with [the user guide](introduction-user-guide.md) and finish with [the programming example](programming-example.md).

## One library

All functionality is provided by one single library.
Add the library to your project and you are ready to go! You can either [download a precompiled](https://github.com/tcunit/TcUnit/releases) (ready to install) version of the library or [download the source code](https://www.github.com/tcunit/tcunit).

## MIT-license

The library and all the source code is licensed according to the MIT-license, which is one of the most relaxed software license terms.
The software is completely free and you can use the software in any way you want, be it private or for commercial use as long as you include the MIT license terms with your software.

## Automated test runs

With the additional TcUnit-Runner software, it’s possible to do integrate all your TcUnit tests into a CI/CD software toolchain.
With the aid of automation software such as [Jenkins](https://www.jenkins.io/) or [Azure DevOps](https://azure.microsoft.com/en-us/services/devops/), you can have your tests being run automatically and collect test statistics every time something is changed in your software version control (such as Git or Subversion).
With the aid of automation software such as [Jenkins](https://www.jenkins.io/) or [Azure DevOps](https://azure.microsoft.com/en-us/services/devops/), you can have your tests being run automatically and collect test statistics every time something is changed in your software version control (such as Git or Subversion).
22 changes: 14 additions & 8 deletions docs/introduction-user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ The purpose of this user guide is to be a short tutorial where we will go throug
3. Create test suites and run the tests

## Download & install

The framework can either be downloaded as a [precompiled library](https://github.com/tcunit/TcUnit/releases), or you can download the [source code](https://www.github.com/TcUnit/TcUnit) and compile the library yourself.

### Install from library file

If you’ve downloaded the library, you should have a file called **TcUnit.library** in your computer.
Start your TwinCAT XAE (Visual Studio).
In the menu of Visual Studio select **PLC** and then **Library Repository...**
Expand All @@ -30,6 +32,7 @@ Click on **Install...**, locate the **TcUnit.library** file and double-click on
Now it will install to your TwinCAT-folder, more specifically C:\TwinCAT\3.1\Components\Plc\Managed Libraries\www.tcunit.org\TcUnit\.

### Install from source

If you want to install it from source, make sure that you have a TwinCAT XAE installed.
Next do a GIT-clone on the repository.
Open the folder where you cloned the repo, and open the solution by double-clicking on the TcUnit.sln file in the root of the folder, which will open the project in your TwinCAT XAE environment.
Expand All @@ -43,6 +46,7 @@ This will install the library on your computer.
Once the library is installed, the file that you saved on the desktop can be removed.

### Reference the library in project

In order to use TcUnit you need to add a reference to the library in your project.
Open your TwinCAT project, and right-click on the **References** under the PLC-project and click on **Add library...**

Expand All @@ -51,6 +55,7 @@ Open your TwinCAT project, and right-click on the **References** under the PLC-p
Next go to the TcUnit-group, select TcUnit and click **OK**.

### Create test suites and run them

For every function block (or free function) that you have defined we want to create a test function block (test suite), which has the responsibility to:

- Instantiate the FB under test
Expand Down Expand Up @@ -78,7 +83,7 @@ Each test suite is responsible of testing one FB or function, and can have one o
Let’s assume we want to create the simplest possible FB that takes two unsigned integers and sums them.
We can create the header for the FB, but the actual implementation can (and should) wait after we’ve done the unit tests.

```
```StructuredText
FUNCTION_BLOCK FB_Sum
VAR_INPUT
one : UINT;
Expand All @@ -92,7 +97,7 @@ END_VAR
Now let’s create the test suite for this.
This FB needs to extend `TcUnit.FB_TestSuite`.

```
```StructuredText
FUNCTION_BLOCK FB_Sum_Test EXTENDS TcUnit.FB_TestSuite
VAR
END_VAR
Expand All @@ -108,7 +113,7 @@ For them well-named tests are invaluable.
We’ll be creating two tests called `TwoPlusTwoEqualsFour` and `ZeroPlusZeroEqualsZero`.
The `TwoPlusTwoEqualsFour` will look like this:

```
```StructuredText
METHOD TwoPlusTwoEqualsFour
VAR
Sum : FB_Sum;
Expand Down Expand Up @@ -136,7 +141,7 @@ This gives the flexibility to have tests that span over more than one PLC-cycle.

For `ZeroPlusZeroEqualsZero` it’s more or less the same code.

```
```StructuredText
METHOD ZeroPlusZeroEqualsZero
VAR
Sum : FB_Sum;
Expand All @@ -156,7 +161,8 @@ TEST_FINISHED();
```

Next we need to update the body of the test suite (`FB_Sum_Test`) to make sure these two tests are being run.
```

```StructuredText
TwoPlusTwoEqualsFour();
ZeroPlusZeroEqualsZero();
```
Expand All @@ -168,7 +174,7 @@ Being part of the library project we only want a convenient way to test all the
`PRG_TEST` needs to instantiate all the test suites, and only execute one line of code.
In this case we only have one test suite.

```
```StructuredText
PROGRAM PRG_TEST
VAR
fbSum_Test : FB_Sum_Test; // This is our test suite
Expand All @@ -191,7 +197,7 @@ As we can see, the test `TwoPlusTwoEqualsFour` failed, which means that the one
The reason this succeeds is that the default return value for an output-parameter is zero, and thus it means that even if we haven’t written the body of `FB_Sum` the test will succeed.
Let’s finish by implementing the body of `FB_Sum`.

```
```StructuredText
FUNCTION_BLOCK FB_Sum
VAR_INPUT
one : UINT;
Expand All @@ -217,4 +223,4 @@ Obviously this is a very simple example and the purpose of this was to show how
Simple functionality that does not require any state would be better suited to be implemented as a function, or in this case just using the "+" operator.
For a real-world example see the [programming example](programming-example.md).

The source code for this example is [available on GitHub](https://github.com/tcunit/ExampleProjects/tree/master/SimpleExampleProject).
The source code for this example is [available on GitHub](https://github.com/tcunit/ExampleProjects/tree/master/SimpleExampleProject).
Loading

0 comments on commit bb35db2

Please sign in to comment.