Files
esphome/tests/unit_tests/test_cpp_generator.py
T
J. Nick Koston c58a26dce2 [core] Add cg.shared_progmem_array for deduplicated PROGMEM tables
Emits one PROGMEM array per distinct type and contents and returns the
same variable to later callers. The array name is made unique against
every config id and registered variable, so a generated table can never
collide with a user id.
2026-10-02 20:45:23 -05:00

840 lines
27 KiB
Python

from collections.abc import Iterator
import math
import pytest
from esphome import cpp_generator as cg, cpp_types as ct
from esphome.core import CORE, ID
class TestExpressions:
@pytest.mark.parametrize(
"target, expected",
(
(cg.RawExpression("foo && bar"), "foo && bar"),
(cg.AssignmentExpression(None, None, "foo", "bar"), 'foo = "bar"'),
(cg.AssignmentExpression(ct.float_, "*", "foo", 1), "float *foo = 1"),
(cg.AssignmentExpression(ct.float_, "", "foo", 1), "float foo = 1"),
(cg.VariableDeclarationExpression(ct.int32, "*", "foo"), "int32_t *foo"),
(cg.VariableDeclarationExpression(ct.int32, "", "foo"), "int32_t foo"),
(cg.ParameterExpression(ct.std_string, "foo"), "std::string foo"),
),
)
def test_str__simple(self, target: cg.Expression, expected: str):
actual = str(target)
assert actual == expected
class TestExpressionList:
SAMPLE_ARGS = (1, "2", True, None, None)
def test_str(self):
target = cg.ExpressionList(*self.SAMPLE_ARGS)
actual = str(target)
assert actual == '1, "2", true'
def test_iter(self):
target = cg.ExpressionList(*self.SAMPLE_ARGS)
actual = iter(target)
assert isinstance(actual, Iterator)
assert len(tuple(actual)) == 3
class TestTemplateArguments:
SAMPLE_ARGS = (int, 1, "2", True, None, None)
def test_str(self):
target = cg.TemplateArguments(*self.SAMPLE_ARGS)
actual = str(target)
assert actual == '<int32_t, 1, "2", true>'
def test_iter(self):
target = cg.TemplateArguments(*self.SAMPLE_ARGS)
actual = iter(target)
assert isinstance(actual, Iterator)
assert len(tuple(actual)) == 4
class TestCallExpression:
def test_str__no_template_args(self):
target = cg.CallExpression(cg.RawExpression("my_function"), 1, "2", False)
actual = str(target)
assert actual == 'my_function(1, "2", false)'
def test_str__with_template_args(self):
target = cg.CallExpression(
cg.RawExpression("my_function"),
cg.TemplateArguments(int, float),
1,
"2",
False,
)
actual = str(target)
assert actual == 'my_function<int32_t, float>(1, "2", false)'
class TestStaticCastExpression:
def test_str(self):
target = cg.StaticCastExpression(ct.bool_, 42)
actual = str(target)
assert actual == "static_cast<bool>(42)"
class TestStructInitializer:
def test_str(self):
target = cg.StructInitializer(
cg.MockObjClass("foo::MyStruct", parents=()),
("state", "on"),
("min_length", 1),
("max_length", 5),
("foo", None),
)
actual = str(target)
assert (
actual == "foo::MyStruct{\n"
' .state = "on",\n'
" .min_length = 1,\n"
" .max_length = 5,\n"
"}"
)
class TestArrayInitializer:
def test_str__empty(self):
target = cg.ArrayInitializer(None, None)
actual = str(target)
assert actual == "{}"
def test_str__not_multiline(self):
target = cg.ArrayInitializer(1, 2, 3, 4)
actual = str(target)
assert actual == "{1, 2, 3, 4}"
def test_str__multiline(self):
target = cg.ArrayInitializer(1, 2, 3, 4, multiline=True)
actual = str(target)
assert actual == "{\n 1,\n 2,\n 3,\n 4,\n}"
class TestParameterListExpression:
def test_str(self):
target = cg.ParameterListExpression(
cg.ParameterExpression(int, "foo"),
(float, "bar"),
)
actual = str(target)
assert actual == "int32_t foo, float bar"
class TestLambdaExpression:
def test_str__no_return(self):
target = cg.LambdaExpression(
(
"if ((foo == 5) && (bar < 10))) {\n",
"}",
),
((int, "foo"), (float, "bar")),
)
actual = str(target)
assert actual == (
"[=](int32_t foo, float bar) {\n if ((foo == 5) && (bar < 10))) {\n }\n}"
)
def test_str__with_return(self):
target = cg.LambdaExpression(
("return (foo == 5) && (bar < 10));",),
cg.ParameterListExpression((int, "foo"), (float, "bar")),
"=",
bool,
)
actual = str(target)
assert actual == (
"[=](int32_t foo, float bar) -> bool {\n"
" return (foo == 5) && (bar < 10));\n"
"}"
)
def test_str__stateless_no_return(self):
"""Test stateless lambda (empty capture) generates correctly"""
target = cg.LambdaExpression(
('ESP_LOGD("main", "Test message");',),
(), # No parameters
"", # Empty capture (stateless)
)
actual = str(target)
assert actual == ('[]() {\n ESP_LOGD("main", "Test message");\n}')
def test_str__stateless_with_return(self):
"""Test stateless lambda with return type generates correctly"""
target = cg.LambdaExpression(
("return global_value > 0;",),
(), # No parameters
"", # Empty capture (stateless)
bool, # Return type
)
actual = str(target)
assert actual == ("[]() -> bool {\n return global_value > 0;\n}")
def test_str__stateless_with_params(self):
"""Test stateless lambda with parameters generates correctly"""
target = cg.LambdaExpression(
("return foo + bar;",),
((int, "foo"), (float, "bar")),
"", # Empty capture (stateless)
float,
)
actual = str(target)
assert actual == (
"[](int32_t foo, float bar) -> float {\n return foo + bar;\n}"
)
def test_str__with_capture(self):
"""Test lambda with capture generates correctly"""
target = cg.LambdaExpression(
("return captured_var + x;",),
((int, "x"),),
"captured_var", # Has capture (not stateless)
int,
)
actual = str(target)
assert actual == (
"[captured_var](int32_t x) -> int32_t {\n return captured_var + x;\n}"
)
class TestCallLambda:
"""Tests for the call_lambda() function."""
def test_call_lambda__return_expression_casts_to_return_type(self):
"""A lambda body that is just a return statement reduces to the
expression, cast to the lambda's return type."""
lamb = cg.LambdaExpression(("return foo + 1;",), (), "", ct.bool_)
result = cg.call_lambda(lamb)
assert isinstance(result, cg.StaticCastExpression)
assert str(result) == "static_cast<bool>(foo + 1)"
def test_call_lambda__return_with_trailing_statements_is_called(self) -> None:
"""Only a lone return statement reduces; a longer body is called as is."""
lamb = cg.LambdaExpression(("return 1;\nfoo();",), (), "", ct.int_)
result = cg.call_lambda(lamb)
assert isinstance(result, cg.CallExpression)
assert str(result).endswith("}()")
def test_call_lambda__braced_return_is_called(self) -> None:
"""A braced return needs the lambda's return type, so it is not reduced."""
lamb = cg.LambdaExpression(("return {};",), (), "", ct.int_)
result = cg.call_lambda(lamb)
assert isinstance(result, cg.CallExpression)
assert "static_cast" not in str(result)
def test_call_lambda__return_expression_with_class_return_type_no_cast(self):
"""A class return type is not cast, since static_cast doesn't apply
to arbitrary class types."""
mock_class = cg.MockObjClass("foo::Bar", parents=())
lamb = cg.LambdaExpression(("return get_bar();",), (), "", mock_class)
result = cg.call_lambda(lamb)
assert isinstance(result, cg.RawExpression)
assert str(result) == "get_bar()"
def test_call_lambda__no_return_with_parameters_calls_with_names(self):
"""A multi-statement lambda with parameters is called with the
parameter names as arguments."""
lamb = cg.LambdaExpression(
("do_something(x, y);",), ((int, "x"), (float, "y")), "=", ct.bool_
)
result = cg.call_lambda(lamb)
assert isinstance(result, cg.CallExpression)
assert str(result) == (
"[=](int32_t x, float y) -> bool {\n do_something(x, y);\n}(x, y)"
)
def test_call_lambda__no_return_type_raises(self):
"""Calling a lambda with no declared return type is a developer
error: call_lambda is only for value-returning lambdas."""
lamb = cg.LambdaExpression(("do_something();",), (), "=")
with pytest.raises(AssertionError):
cg.call_lambda(lamb)
def test_call_lambda__identifier_starting_with_return_is_not_a_return_statement(
self,
):
"""A body that merely starts with the substring "return" (e.g. a call
to a function named returnValue()) must not be mistaken for a return
statement -- the match requires a word boundary after "return"."""
lamb = cg.LambdaExpression(("returnValue();",), (), "=", ct.bool_)
result = cg.call_lambda(lamb)
assert isinstance(result, cg.CallExpression)
assert str(result) == "[=]() -> bool {\n returnValue();\n}()"
def test_call_lambda__no_return_no_parameters_calls_with_no_args(self):
"""A multi-statement lambda without parameters is called with no
arguments."""
lamb = cg.LambdaExpression(("do_something();",), (), "", ct.bool_)
result = cg.call_lambda(lamb)
assert isinstance(result, cg.CallExpression)
assert str(result) == "[]() -> bool {\n do_something();\n}()"
class TestLiterals:
@pytest.mark.parametrize(
"target, expected",
(
(cg.StringLiteral("foo"), '"foo"'),
(cg.IntLiteral(0), "0"),
(cg.IntLiteral(42), "42"),
(cg.IntLiteral(4304967295), "4304967295ULL"),
(cg.IntLiteral(2150483647), "2150483647UL"),
(cg.IntLiteral(-2150083647), "-2150083647LL"),
(cg.BoolLiteral(True), "true"),
(cg.BoolLiteral(False), "false"),
(cg.HexIntLiteral(0), "0x00"),
(cg.HexIntLiteral(42), "0x2A"),
(cg.HexIntLiteral(682), "0x2AA"),
(cg.FloatLiteral(0.0), "0.0f"),
(cg.FloatLiteral(4.2), "4.2f"),
(cg.FloatLiteral(1.23456789), "1.23456789f"),
(cg.FloatLiteral(math.nan), "NAN"),
(cg.FlashStringLiteral("hello"), 'ESPHOME_F("hello")'),
(cg.FlashStringLiteral(""), 'ESPHOME_F("")'),
(
cg.FlashStringLiteral('quote"here'),
'ESPHOME_F("quote\\042here")',
),
),
)
def test_str__simple(self, target: cg.Literal, expected: str):
actual = str(target)
assert actual == expected
FAKE_ENUM_VALUE = cg.EnumValue()
FAKE_ENUM_VALUE.enum_value = "foo"
@pytest.mark.parametrize(
"obj, expected_type",
(
(cg.RawExpression("foo"), cg.RawExpression),
(FAKE_ENUM_VALUE, cg.StringLiteral),
(True, cg.BoolLiteral),
("foo", cg.StringLiteral),
(cg.HexInt(42), cg.HexIntLiteral),
(42, cg.IntLiteral),
(42.1, cg.FloatLiteral),
(cg.TimePeriodMicroseconds(microseconds=42), cg.IntLiteral),
(cg.TimePeriodMilliseconds(milliseconds=42), cg.IntLiteral),
(cg.TimePeriodSeconds(seconds=42), cg.IntLiteral),
(cg.TimePeriodMinutes(minutes=42), cg.IntLiteral),
((1, 2, 3), cg.ArrayInitializer),
([1, 2, 3], cg.ArrayInitializer),
),
)
def test_safe_exp__allowed_values(obj, expected_type):
actual = cg.safe_exp(obj)
assert isinstance(actual, expected_type)
@pytest.mark.parametrize(
"obj, expected_type",
(
(bool, ct.bool_),
(int, ct.int32),
(float, ct.float_),
),
)
def test_safe_exp__allowed_types(obj, expected_type):
actual = cg.safe_exp(obj)
assert actual is expected_type
@pytest.mark.parametrize(
"obj, expected_error",
(
(cg.ID("foo"), "Object foo is an ID."),
((x for x in "foo"), r"Object <.*> is a coroutine."),
(None, "Object is not an expression"),
),
)
def test_safe_exp__invalid_values(obj, expected_error):
with pytest.raises(ValueError, match=expected_error):
cg.safe_exp(obj)
class TestStatements:
@pytest.mark.parametrize(
"target, expected",
(
(cg.RawStatement("foo && bar"), "foo && bar"),
(cg.ExpressionStatement("foo"), '"foo";'),
(cg.ExpressionStatement(42), "42;"),
(cg.LineComment("The point of foo is..."), "// The point of foo is..."),
(
cg.LineComment("Help help\nI'm being repressed"),
"// Help help\n// I'm being repressed",
),
(
cg.ProgmemAssignmentExpression(ct.uint16, "foo", "bar"),
'static constexpr uint16_t foo[] PROGMEM = "bar"',
),
),
)
def test_str__simple(self, target: cg.Statement, expected: str):
actual = str(target)
assert actual == expected
# TODO: This method has side effects in CORE
# def test_progmem_array():
# pass
class TestMockObj:
def test_getattr(self):
target = cg.MockObj("foo")
actual = target.eek
assert isinstance(actual, cg.MockObj)
assert actual.base == "foo.eek"
assert actual.op == "."
class TestStatementFunction:
"""Tests for the statement() function."""
def test_statement__expression_converted_to_statement(self):
"""Test that expressions are converted to ExpressionStatement."""
expr = cg.RawExpression("foo()")
result = cg.statement(expr)
assert isinstance(result, cg.ExpressionStatement)
assert str(result) == "foo();"
def test_statement__statement_unchanged(self):
"""Test that statements are returned unchanged."""
stmt = cg.RawStatement("foo()")
result = cg.statement(stmt)
assert result is stmt
assert str(result) == "foo()"
def test_statement__expression_statement_unchanged(self):
"""Test that ExpressionStatement is returned unchanged."""
stmt = cg.ExpressionStatement(42)
result = cg.statement(stmt)
assert result is stmt
assert str(result) == "42;"
def test_statement__line_comment_unchanged(self):
"""Test that LineComment is returned unchanged."""
stmt = cg.LineComment("This is a comment")
result = cg.statement(stmt)
assert result is stmt
assert str(result) == "// This is a comment"
class TestLiteralFunction:
"""Tests for the literal() function."""
def test_literal__creates_mockobj(self):
"""Test that literal() creates a MockObj."""
result = cg.literal("MY_CONSTANT")
assert isinstance(result, cg.MockObj)
assert result.base == "MY_CONSTANT"
assert result.op == ""
def test_literal__string_representation(self):
"""Test that literal names appear unquoted in generated code."""
result = cg.literal("nullptr")
assert str(result) == "nullptr"
def test_literal__can_be_used_in_expressions(self):
"""Test that literals can be used as part of larger expressions."""
null_lit = cg.literal("nullptr")
expr = cg.CallExpression(cg.RawExpression("my_func"), null_lit)
assert str(expr) == "my_func(nullptr)"
def test_literal__common_cpp_literals(self):
"""Test common C++ literal values."""
test_cases = [
("nullptr", "nullptr"),
("true", "true"),
("false", "false"),
("NULL", "NULL"),
("NAN", "NAN"),
]
for name, expected in test_cases:
result = cg.literal(name)
assert str(result) == expected
class TestLambdaConstructor:
"""Tests for the Lambda class constructor in core/__init__.py."""
def test_lambda__from_string(self):
"""Test Lambda constructor with string argument."""
from esphome.core import Lambda
lambda_obj = Lambda("return x + 1;")
assert lambda_obj.value == "return x + 1;"
assert str(lambda_obj) == "return x + 1;"
def test_lambda__from_expression(self):
"""Test Lambda constructor with Expression argument."""
from esphome.core import Lambda
expr = cg.RawExpression("x + 1")
lambda_obj = Lambda(expr)
# Expression should be converted to statement (with semicolon)
assert lambda_obj.value == "x + 1;"
def test_lambda__from_lambda(self):
"""Test Lambda constructor with another Lambda argument."""
from esphome.core import Lambda
original = Lambda("return x + 1;")
copy = Lambda(original)
assert copy.value == original.value
assert copy.value == "return x + 1;"
def test_lambda__parts_parsing(self):
"""Test that Lambda correctly parses parts with id() references."""
from esphome.core import Lambda
lambda_obj = Lambda("return id(my_sensor).state;")
parts = lambda_obj.parts
# Parts should be split by LAMBDA_PROG regex: text, id, op, text
assert len(parts) == 4
assert parts[0] == "return "
assert parts[1] == "my_sensor"
assert parts[2] == "."
assert parts[3] == "state;"
def test_lambda__requires_ids(self):
"""Test that Lambda correctly extracts required IDs."""
from esphome.core import ID, Lambda
lambda_obj = Lambda("return id(sensor1).state + id(sensor2).value;")
ids = lambda_obj.requires_ids
assert len(ids) == 2
assert all(isinstance(id_obj, ID) for id_obj in ids)
assert ids[0].id == "sensor1"
assert ids[1].id == "sensor2"
def test_lambda__no_ids(self):
"""Test Lambda with no id() references."""
from esphome.core import Lambda
lambda_obj = Lambda("return 42;")
ids = lambda_obj.requires_ids
assert len(ids) == 0
def test_lambda__comment_removal(self):
"""Test that comments are removed when parsing parts."""
from esphome.core import Lambda
lambda_obj = Lambda("return id(sensor).state; // Get sensor state")
parts = lambda_obj.parts
# Comment should be replaced with space, not affect parsing
assert "my_sensor" not in str(parts)
def test_lambda__multiline_string(self):
"""Test Lambda with multiline string."""
from esphome.core import Lambda
code = """if (id(sensor).state > 0) {
return true;
}
return false;"""
lambda_obj = Lambda(code)
assert lambda_obj.value == code
assert "sensor" in [id_obj.id for id_obj in lambda_obj.requires_ids]
@pytest.mark.asyncio
class TestProcessLambda:
"""Tests for the process_lambda() async function."""
async def test_process_lambda__none_value(self):
"""Test that None returns None."""
result = await cg.process_lambda(None, [])
assert result is None
async def test_process_lambda__with_expression(self):
"""Test process_lambda with Expression argument."""
expr = cg.RawExpression("return x + 1")
result = await cg.process_lambda(expr, [(int, "x")])
assert isinstance(result, cg.LambdaExpression)
assert "x + 1" in str(result)
async def test_process_lambda__simple_lambda_no_ids(self):
"""Test process_lambda with simple Lambda without id() references."""
from esphome.core import Lambda
lambda_obj = Lambda("return x + 1;")
result = await cg.process_lambda(lambda_obj, [(int, "x")])
assert isinstance(result, cg.LambdaExpression)
# Should have parameter
lambda_str = str(result)
assert "int32_t x" in lambda_str
assert "return x + 1;" in lambda_str
async def test_process_lambda__with_return_type(self):
"""Test process_lambda with return type specified."""
from esphome.core import Lambda
lambda_obj = Lambda("return x > 0;")
result = await cg.process_lambda(lambda_obj, [(int, "x")], return_type=bool)
assert isinstance(result, cg.LambdaExpression)
lambda_str = str(result)
assert "-> bool" in lambda_str
async def test_process_lambda__with_capture(self):
"""Test process_lambda with capture specified."""
from esphome.core import Lambda
lambda_obj = Lambda("return captured + x;")
result = await cg.process_lambda(lambda_obj, [(int, "x")], capture="captured")
assert isinstance(result, cg.LambdaExpression)
lambda_str = str(result)
assert "[captured]" in lambda_str
async def test_process_lambda__empty_capture(self):
"""Test process_lambda with empty capture (stateless lambda)."""
from esphome.core import Lambda
lambda_obj = Lambda("return x + 1;")
result = await cg.process_lambda(lambda_obj, [(int, "x")], capture="")
assert isinstance(result, cg.LambdaExpression)
lambda_str = str(result)
assert "[]" in lambda_str
async def test_process_lambda__no_parameters(self):
"""Test process_lambda with no parameters."""
from esphome.core import Lambda
lambda_obj = Lambda("return 42;")
result = await cg.process_lambda(lambda_obj, [])
assert isinstance(result, cg.LambdaExpression)
lambda_str = str(result)
# Should have empty parameter list
assert "()" in lambda_str
async def test_process_lambda__multiple_parameters(self):
"""Test process_lambda with multiple parameters."""
from esphome.core import Lambda
lambda_obj = Lambda("return x + y + z;")
result = await cg.process_lambda(
lambda_obj, [(int, "x"), (float, "y"), (bool, "z")]
)
assert isinstance(result, cg.LambdaExpression)
lambda_str = str(result)
assert "int32_t x" in lambda_str
assert "float y" in lambda_str
assert "bool z" in lambda_str
async def test_process_lambda__parameter_validation(self):
"""Test that malformed parameters raise assertion error."""
from esphome.core import Lambda
lambda_obj = Lambda("return x;")
# Test invalid parameter format (not list of tuples)
with pytest.raises(AssertionError):
await cg.process_lambda(lambda_obj, "invalid")
# Test invalid tuple format (not 2-element tuples)
with pytest.raises(AssertionError):
await cg.process_lambda(lambda_obj, [(int, "x", "extra")])
# Test invalid tuple format (single element)
with pytest.raises(AssertionError):
await cg.process_lambda(lambda_obj, [(int,)])
@pytest.mark.asyncio
async def test_templatable__string_with_std_string_returns_flash_literal() -> None:
"""Static string with std::string output_type returns FlashStringLiteral."""
result = await cg.templatable("hello", [], ct.std_string)
assert isinstance(result, cg.FlashStringLiteral)
assert str(result) == 'ESPHOME_F("hello")'
@pytest.mark.asyncio
async def test_templatable__empty_string_with_std_string() -> None:
"""Empty static string with std::string output_type returns FlashStringLiteral."""
result = await cg.templatable("", [], ct.std_string)
assert isinstance(result, cg.FlashStringLiteral)
assert str(result) == 'ESPHOME_F("")'
@pytest.mark.asyncio
async def test_templatable__string_with_none_output_type() -> None:
"""Static string with output_type=None returns raw string (no wrapping)."""
result = await cg.templatable("hello", [], None)
assert isinstance(result, str)
assert result == "hello"
@pytest.mark.asyncio
async def test_templatable__int_with_std_string() -> None:
"""Non-string value with std::string output_type returns raw value."""
result = await cg.templatable(42, [], ct.std_string)
assert result == 42
@pytest.mark.asyncio
async def test_templatable__string_with_non_string_output_type() -> None:
"""Static string with non-std::string output_type returns stateless lambda."""
result = await cg.templatable("hello", [], ct.bool_)
assert isinstance(result, cg.LambdaExpression)
assert result.capture == ""
@pytest.mark.asyncio
async def test_templatable__with_to_exp_callable() -> None:
"""When to_exp is provided, it is applied to non-template values."""
result = await cg.templatable(42, [], None, to_exp=lambda x: x * 2)
assert result == 84
@pytest.mark.asyncio
async def test_templatable__with_to_exp_callable_and_output_type() -> None:
"""When to_exp is provided with non-string output_type, result is lambda-wrapped."""
result = await cg.templatable(42, [], ct.int_, to_exp=lambda x: x * 2)
assert isinstance(result, cg.LambdaExpression)
assert result.capture == ""
@pytest.mark.asyncio
async def test_templatable__with_to_exp_dict() -> None:
"""When to_exp is a dict, value is looked up."""
mapping: dict[str, int] = {"on": 1, "off": 0}
result = await cg.templatable("on", [], None, to_exp=mapping)
assert result == 1
@pytest.mark.asyncio
async def test_templatable__lambda_with_std_string() -> None:
"""Lambda value returns LambdaExpression, not FlashStringLiteral."""
from esphome.core import Lambda
lambda_obj = Lambda('return "hello";')
result = await cg.templatable(lambda_obj, [], ct.std_string)
assert isinstance(result, cg.LambdaExpression)
class TestSharedProgmemArray:
def test_identical_contents_share_one_array(self) -> None:
CORE.config = {}
a = cg.shared_progmem_array("table", ct.uint8, [1, 2, 3])
b = cg.shared_progmem_array("table", ct.uint8, [1, 2, 3])
c = cg.shared_progmem_array("table", ct.uint8, [4])
assert a is b
assert str(a) != str(c)
assert sum("PROGMEM" in str(st) for st in CORE.main_statements) == 2
def test_same_contents_different_type_are_separate(self) -> None:
CORE.config = {}
a = cg.shared_progmem_array("table", ct.uint8, [1])
b = cg.shared_progmem_array("table", ct.uint16, [1])
assert str(a) != str(b)
def test_name_avoids_config_ids(self) -> None:
CORE.config = {"sensor": [{"id": ID("table", is_declaration=True)}]}
array = cg.shared_progmem_array("table", ct.uint8, [1])
assert str(array) == "table_2"
def test_name_avoids_registered_variables(self) -> None:
CORE.config = {}
CORE.register_variable(ID("table", is_declaration=True), cg.MockObj("table"))
array = cg.shared_progmem_array("table", ct.uint8, [1])
assert str(array) == "table_2"