Skip to content

Conversation

@jonrebm
Copy link
Contributor

@jonrebm jonrebm commented Dec 14, 2025

I've done some cleanup in the microcom codebase, and created a CI workflow to catch many of the encountered issues in the future.

These tests fail now, as long as the PRs resolving the issues are still open:

The quiet switch shall silence all printing to stdout during normal
operation, but leaves the interactive console and all error messages
functional.

Useful for automation such as in CI.

Signed-off-by: Jonas Rebmann <[email protected]>
I set up those tests for the issues with the current telnet
implementation. They can serve as regression tests after the issues have
been resolved.

Add them as the first integration tests.
Add pythons bytecode cache files to gitignore.

Signed-off-by: Jonas Rebmann <[email protected]>
 - correct branch to main, which our default branch
 - fix dependency installation
 - invoke pytest
 - don't build an unneeded release tarball

Signed-off-by: Jonas Rebmann <[email protected]>
This has proven to be useful to catch out-of-bounds access and I assume
will prevent many cases of success by chance when testing microcom.

Signed-off-by: Jonas Rebmann <[email protected]>
The rules are rather flexible so they should be enforcible treewide. If
not, maybe there's a way to at least only enforce it on the sections or
at least files that the commits touched?

Signed-off-by: Jonas Rebmann <[email protected]>
Amongst others, this catches uninitialized memory access, which libasan
does not.

Signed-off-by: Jonas Rebmann <[email protected]>
Comment on lines +152 to +200
case '?':
main_usage(1, "", "");
break;
case 'h':
main_usage(0, "", "");
break;
case 'v':
printf("%s\n", PACKAGE_VERSION);
exit(EXIT_SUCCESS);
break;
case 'p':
device = optarg;
break;
case 's':
current_speed = strtoul(optarg, NULL, 0);
break;
case 't':
telnet = 1;
hostport = optarg;
break;
case 'c':
can = 1;
interfaceid = optarg;
break;
case 'f':
opt_force = 1;
break;
case 'd':
debug = 1;
break;
case 'q':
quiet = 1;
break;
case 'l':
logfile = optarg;
break;
case 'o':
listenonly = 1;
break;
case 'a':
answerback = optarg;
break;
case 'e':
if (strlen(optarg) != 1) {
fprintf(stderr, "Option -e requires a single character argument.\n");
exit(EXIT_FAILURE);
}
escape_char = *optarg;
break;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This mixes a formatting change with a code change. It would be great to only have the

+		case 'q':
+			quiet = 1;
+			break;

In this commit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally based this on all the three dependent branches and only haphazardly rebased to main to avoid making too large a pull request.
The diff here will be more meaningful once #55 is closed. I'm converting this pr to draft until then.


struct ios_ops *ios;
int debug;
int quiet;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#55 explicitly zeros debug. Not zeroing quiet now aligns it with the code as-is, but since #55 should be merged first I think this should be: int quiet = 0;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it's an artifact from my attempt to split up what was originally a single series. quiet is zero-initialized on the "single series" branch to which I can update this pr once the dependent PRs are merged.

I suppose unitialized globals are more a bad style practice than actual reliance on unitialized memory, at least on linux where they're zero-initialized as part of the bss (in contrast to locals which are stack-allocated and not automatically initialized).

Anyway, yes this will be taken care of :)

Comment on lines +28 to +30
conn.sendall(buf)
time.sleep(0.01) # sadly without this, microcom may miss the transmission
sock.close()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this hardcoded sleep result in flaky tests in the future?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, definitely. I'm not sure why this is even needed. I'd expect that expect blocks until microcom has connected via TCP and that from this point whatever the server sends should definitely reach microcom. I suspect this is actually a workaround for a microcom bug.
As long as it's needed, should 0.01 turn out flaky on a slow runner, 0.1 should still run the tests quick enough.

But yes bad bad hack!

Comment on lines +63 to +65
@pytest.mark.parametrize("buf", [10, 1023, 1024, 1025, 4000])
def test_no_cmd(telnet_recv, buf):
payload = make_pattern(buf)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@pytest.mark.parametrize("buf", [10, 1023, 1024, 1025, 4000])
def test_no_cmd(telnet_recv, buf):
payload = make_pattern(buf)
@pytest.mark.parametrize("length", [10, 1023, 1024, 1025, 4000])
def test_no_cmd(telnet_recv, length):
payload = make_pattern(length)

@hnez
Copy link
Member

hnez commented Dec 16, 2025

I like it! Since this adds python code to the repository: should the CI also run e.g. ruff format and ruff check now?

@jonrebm jonrebm marked this pull request as draft December 16, 2025 13:56
Comment on lines +17 to +19
@pytest.fixture
def telnet_recv(cmd):
def _recv(buf, timeout=1):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Define helper functions directly; there is no need to use fixtures for that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason that this is a fixture and not a helper function is that it directly depends on the cmd fixture. My understanding is that only tests and fixtures can depend on other fixtures, therefore a fixture is adequate here.

As there's little reason why a test that uses telnet_recv would also use cmd, there is no reason to pass the cmd fixture to the test only to then pass it on to telnet_recv.
So I'd prefer this:

def test_a(telnet_recv):
    assert telnet_recv("abc") == "abc"

Over this:

def test_a(cmd):
    assert telnet_recv(cmd, "abc") == "abc"

The difference is negligible in this case but adding more dependent fixtures to telnet_recv in the latter solution would be suboptimal, because they'd all need to be added to each of the tests even if the test code itself isn't touched

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a fixture for helper functions such as these hides that telnet_recv actually uses the cmd fixture, making the code unnecessarily hard to read.

@jonrebm
Copy link
Contributor Author

jonrebm commented Dec 16, 2025

Thanks!
Well at least I could run ruff format next time I update this branch... But yes I'll add it to CI too if that's preinstalled on the runner atleast

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants