docs: adding tutorial 4
This commit is contained in:
parent
75a92f3a38
commit
c554983436
4 changed files with 181 additions and 0 deletions
121
docs/tutorials/tutorial4.md
Normal file
121
docs/tutorials/tutorial4.md
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
# Tutorial 4 - Tests
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
A use case for CORE would be to help automate integration tests for running
|
||||||
|
software within a network. This tutorial covers using CORE with the python
|
||||||
|
pytest testing framework. It will show how you can define tests, for different
|
||||||
|
use cases to validate software and outcomes within a defined network. Using
|
||||||
|
pytest, you would create tests using all the standard pytest functionality.
|
||||||
|
Creating a test file, and then defining test functions to run. For these tests,
|
||||||
|
we are leveraging the CORE library directly and the API it provides.
|
||||||
|
|
||||||
|
Refer to the [pytest documentation](https://docs.pytest.org) for indepth
|
||||||
|
information on how to write tests with pytest.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
A directory is used for containing your tests. Within this directory we need a
|
||||||
|
**conftest.py**, which pytest will pick up to help define and provide
|
||||||
|
test fixtures, which will be leveraged within our tests.
|
||||||
|
|
||||||
|
* tests
|
||||||
|
* conftest.py - file used by pytest to define fixtures, which can be shared across tests
|
||||||
|
* test_ping.py - defines test classes/functions to run
|
||||||
|
|
||||||
|
## Test Fixtures
|
||||||
|
|
||||||
|
Below are the definitions for fixture you can define to facilitate and make
|
||||||
|
creating CORE based tests easier.
|
||||||
|
|
||||||
|
The global session fixture creates one **CoreEmu** object for the entire
|
||||||
|
test session, yields it for testing, and calls shutdown when everything
|
||||||
|
is over.
|
||||||
|
|
||||||
|
``` python
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def global_session():
|
||||||
|
core = CoreEmu()
|
||||||
|
session = core.create_session()
|
||||||
|
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||||
|
yield session
|
||||||
|
core.shutdown()
|
||||||
|
```
|
||||||
|
|
||||||
|
The regular session fixture leverages the global session fixture. It
|
||||||
|
will set the correct state for each test case, yield the session for a test,
|
||||||
|
and then clear the session after a test finishes to prepare for the next
|
||||||
|
test.
|
||||||
|
|
||||||
|
``` python
|
||||||
|
@pytest.fixture
|
||||||
|
def session(global_session):
|
||||||
|
global_session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||||
|
yield global_session
|
||||||
|
global_session.clear()
|
||||||
|
```
|
||||||
|
|
||||||
|
The ip prefixes fixture help provide a preconfigured convenience for
|
||||||
|
creating and assigning interfaces to nodes, when creating your network
|
||||||
|
within a test. The address subnet can be whatever you desire.
|
||||||
|
|
||||||
|
``` python
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def ip_prefixes():
|
||||||
|
return IpPrefixes(ip4_prefix="10.0.0.0/24")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Functions
|
||||||
|
|
||||||
|
Within a pytest test file, you have the freedom to create any kind of
|
||||||
|
test you like, but they will all follow a similar formula.
|
||||||
|
|
||||||
|
* define a test function that will leverage the session and ip prefixes fixtures
|
||||||
|
* then create a network to test, using the session fixture
|
||||||
|
* run commands within nodes as desired, to test out your use case
|
||||||
|
* validate command result or output for expected behavior to pass or fail
|
||||||
|
|
||||||
|
In the test below, we create a simple 2 node wired network and validate
|
||||||
|
node1 can ping node2 successfully.
|
||||||
|
|
||||||
|
``` python
|
||||||
|
def test_success(self, session: Session, ip_prefixes: IpPrefixes):
|
||||||
|
# create nodes
|
||||||
|
node1 = session.add_node(CoreNode)
|
||||||
|
node2 = session.add_node(CoreNode)
|
||||||
|
|
||||||
|
# link nodes together
|
||||||
|
iface1_data = ip_prefixes.create_iface(node1)
|
||||||
|
iface2_data = ip_prefixes.create_iface(node2)
|
||||||
|
session.add_link(node1.id, node2.id, iface1_data, iface2_data)
|
||||||
|
|
||||||
|
# ping node, expect a successful command
|
||||||
|
node1.cmd(f"ping -c 1 {iface2_data.ip4}")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Install Pytest
|
||||||
|
|
||||||
|
Since we are running an automated test within CORE, we will need to install
|
||||||
|
pytest within the python interpreter used by CORE.
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
sudo /opt/core/venv/bin/python -m pip install pytest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running Tests
|
||||||
|
|
||||||
|
You can run your own or the provided tests, by running the following.
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
cd <test directory>
|
||||||
|
sudo /opt/core/venv/bin/python -m pytest -v
|
||||||
|
```
|
||||||
|
|
||||||
|
If you run the provided tests, you would expect to see the two tests
|
||||||
|
running and passing.
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
tests/test_ping.py::TestPing::test_success PASSED [ 50%]
|
||||||
|
tests/test_ping.py::TestPing::test_failure PASSED [100%]
|
||||||
|
```
|
||||||
|
|
|
@ -41,6 +41,7 @@ nav:
|
||||||
- Tutorials:
|
- Tutorials:
|
||||||
- Setup: tutorials/setup.md
|
- Setup: tutorials/setup.md
|
||||||
- Tutorial 1: tutorials/tutorial1.md
|
- Tutorial 1: tutorials/tutorial1.md
|
||||||
|
- Tutorial 4: tutorials/tutorial4.md
|
||||||
- Detailed Topics:
|
- Detailed Topics:
|
||||||
- GUI: gui.md
|
- GUI: gui.md
|
||||||
- Node Types:
|
- Node Types:
|
||||||
|
|
24
package/examples/tutorials/tutorial4/tests/conftest.py
Normal file
24
package/examples/tutorials/tutorial4/tests/conftest.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import pytest
|
||||||
|
from core.emulator.coreemu import CoreEmu
|
||||||
|
from core.emulator.data import IpPrefixes
|
||||||
|
from core.emulator.enumerations import EventTypes
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def global_session():
|
||||||
|
core = CoreEmu()
|
||||||
|
session = core.create_session()
|
||||||
|
yield session
|
||||||
|
core.shutdown()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def session(global_session):
|
||||||
|
global_session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||||
|
yield global_session
|
||||||
|
global_session.clear()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def ip_prefixes():
|
||||||
|
return IpPrefixes(ip4_prefix="10.0.0.0/24")
|
35
package/examples/tutorials/tutorial4/tests/test_ping.py
Normal file
35
package/examples/tutorials/tutorial4/tests/test_ping.py
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import pytest
|
||||||
|
from core.emulator.data import IpPrefixes, LinkOptions
|
||||||
|
from core.emulator.session import Session
|
||||||
|
from core.errors import CoreCommandError
|
||||||
|
from core.nodes.base import CoreNode
|
||||||
|
|
||||||
|
|
||||||
|
class TestPing:
|
||||||
|
def test_success(self, session: Session, ip_prefixes: IpPrefixes):
|
||||||
|
# create nodes
|
||||||
|
node1 = session.add_node(CoreNode)
|
||||||
|
node2 = session.add_node(CoreNode)
|
||||||
|
|
||||||
|
# link nodes together
|
||||||
|
iface1_data = ip_prefixes.create_iface(node1)
|
||||||
|
iface2_data = ip_prefixes.create_iface(node2)
|
||||||
|
session.add_link(node1.id, node2.id, iface1_data, iface2_data)
|
||||||
|
|
||||||
|
# ping node, expect a successful command
|
||||||
|
node1.cmd(f"ping -c 1 {iface2_data.ip4}")
|
||||||
|
|
||||||
|
def test_failure(self, session: Session, ip_prefixes: IpPrefixes):
|
||||||
|
# create nodes
|
||||||
|
node1 = session.add_node(CoreNode)
|
||||||
|
node2 = session.add_node(CoreNode)
|
||||||
|
|
||||||
|
# link nodes together
|
||||||
|
iface1_data = ip_prefixes.create_iface(node1)
|
||||||
|
iface2_data = ip_prefixes.create_iface(node2)
|
||||||
|
options = LinkOptions(loss=100.0)
|
||||||
|
session.add_link(node1.id, node2.id, iface1_data, iface2_data, options)
|
||||||
|
|
||||||
|
# ping node, expect command to fail and raise exception due to 100% loss
|
||||||
|
with pytest.raises(CoreCommandError):
|
||||||
|
node1.cmd(f"ping -c 1 {iface2_data.ip4}")
|
Loading…
Reference in a new issue