blob: 927f4834746accae405b80d7f3676c3953e5a817 [file]
############################################################################
# SPDX-License-Identifier: Apache-2.0
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
############################################################################
"""Tests for ntfc.pytest.gtest_plugin."""
from types import SimpleNamespace
from typing import Optional
from unittest.mock import MagicMock
import pytest
from ntfc.device.common import CmdReturn, CmdStatus
from ntfc.parsers.base import TestItem as _Item
from ntfc.parsers.gtest import GtestParser
from ntfc.pytest.gtest_plugin import (
GtestParserPlugin,
_discover_gtest_tests,
_items_cache,
)
@pytest.fixture(autouse=True)
def _clear_caches():
GtestParser.clear_session()
_items_cache.clear()
yield
GtestParser.clear_session()
_items_cache.clear()
_GTEST_OUTPUT = (
"[ RUN ] Suite1.test_foo\n"
"[ OK ] Suite1.test_foo (0 ms)\n"
"[ RUN ] Suite1.test_bar\n"
"[ FAILED ] Suite1.test_bar (1 ms)\n"
)
def _make_core(output: str = ""):
"""Build a mock ProductCore returning gtest run output."""
cmd_return = CmdReturn(status=CmdStatus.SUCCESS, output=output)
conf = SimpleNamespace(elf_path="")
return SimpleNamespace(
conf=conf,
sendCommandReadUntilPattern=lambda *_a, **_kw: cmd_return,
)
def _make_node(
binary: Optional[str] = None,
flt: Optional[str] = None,
):
if binary is None:
binary_marker = None
else:
mk_kwargs = {}
if flt is not None:
mk_kwargs["filter"] = flt
binary_marker = SimpleNamespace(args=(binary,), kwargs=mk_kwargs)
node = MagicMock()
node.get_closest_marker.return_value = binary_marker
return node
def _make_metafunc(fixture_names, node=None):
mf = MagicMock()
mf.fixturenames = fixture_names
mf.definition = node or _make_node(binary=None)
return mf
def test_pytest_configure_preserves_caches(monkeypatch):
"""pytest_configure does not modify session cache or items cache."""
core = _make_core(output=_GTEST_OUTPUT)
product_mock = SimpleNamespace(core=lambda _: core)
monkeypatch.setattr(pytest, "product", product_mock, raising=False)
_items_cache[("bin", None)] = [_Item(name="Suite1.test_foo")]
GtestParser._session_results["bin"] = {}
plugin = GtestParserPlugin()
plugin.pytest_configure(MagicMock())
assert _items_cache == {("bin", None): [_Item(name="Suite1.test_foo")]}
assert GtestParser._session_results == {"bin": {}}
def test_generate_tests_no_gtest_fixture():
"""pytest_generate_tests skips when gtest_parser not in fixturenames."""
plugin = GtestParserPlugin()
mf = _make_metafunc(["cmocka_parser"])
plugin.pytest_generate_tests(mf)
mf.parametrize.assert_not_called()
def test_generate_tests_no_marker():
"""pytest_generate_tests skips when no parser_binary marker."""
plugin = GtestParserPlugin()
mf = _make_metafunc(["gtest_parser"], node=_make_node(binary=None))
plugin.pytest_generate_tests(mf)
mf.parametrize.assert_not_called()
def test_generate_tests_empty_discovery(monkeypatch):
"""pytest_generate_tests skips parametrize when no tests found."""
plugin = GtestParserPlugin()
core = _make_core(output="")
product_mock = SimpleNamespace(core=lambda _: core)
monkeypatch.setattr(pytest, "product", product_mock, raising=False)
mf = _make_metafunc(["gtest_parser"], node=_make_node(binary="empty_bin"))
plugin.pytest_generate_tests(mf)
mf.parametrize.assert_not_called()
def test_generate_tests_parametrizes(monkeypatch):
"""pytest_generate_tests parametrizes gtest_parser with test names."""
plugin = GtestParserPlugin()
core = _make_core(output=_GTEST_OUTPUT)
product_mock = SimpleNamespace(core=lambda _: core)
monkeypatch.setattr(pytest, "product", product_mock, raising=False)
mf = _make_metafunc(["gtest_parser"], node=_make_node(binary="gtest_bin"))
plugin.pytest_generate_tests(mf)
mf.parametrize.assert_called_once()
args = mf.parametrize.call_args[0]
assert args[0] == "gtest_parser"
assert "Suite1.test_foo" in args[1]
assert "Suite1.test_bar" in args[1]
def test_generate_tests_with_filter(monkeypatch):
"""pytest_generate_tests applies filter from marker kwargs."""
plugin = GtestParserPlugin()
core = _make_core(output=_GTEST_OUTPUT)
product_mock = SimpleNamespace(core=lambda _: core)
monkeypatch.setattr(pytest, "product", product_mock, raising=False)
mf = _make_metafunc(
["gtest_parser"],
node=_make_node(binary="gtest_bin", flt="Suite1.test_foo"),
)
plugin.pytest_generate_tests(mf)
mf.parametrize.assert_called_once()
args = mf.parametrize.call_args[0]
assert args[1] == ["Suite1.test_foo"]
def test_generate_tests_uses_items_cache(monkeypatch):
"""Second pytest_generate_tests call uses cached items."""
calls = []
def _send(*_a, **_kw):
calls.append(1)
return CmdReturn(status=CmdStatus.SUCCESS, output=_GTEST_OUTPUT)
conf = SimpleNamespace(elf_path="")
core = SimpleNamespace(conf=conf, sendCommandReadUntilPattern=_send)
product_mock = SimpleNamespace(core=lambda _: core)
monkeypatch.setattr(pytest, "product", product_mock, raising=False)
plugin = GtestParserPlugin()
mf = _make_metafunc(["gtest_parser"], node=_make_node(binary="gtest_bin"))
plugin.pytest_generate_tests(mf)
plugin.pytest_generate_tests(mf)
assert len(calls) == 1
def test_gtest_parser_fixture(monkeypatch):
"""gtest_parser fixture returns a GtestParser with cached results."""
plugin = GtestParserPlugin()
core = _make_core(output=_GTEST_OUTPUT)
product_mock = SimpleNamespace(core=lambda _: core)
monkeypatch.setattr(pytest, "product", product_mock, raising=False)
# Populate session cache as discovery would
GtestParser._session_results["gtest_bin"] = {
"Suite1.test_foo": GtestParser(core, "gtest_bin")._parse_output(
_GTEST_OUTPUT
)["Suite1.test_foo"]
}
request = SimpleNamespace(
param="Suite1.test_foo",
node=_make_node(binary="gtest_bin"),
)
parser = plugin.gtest_parser.__wrapped__(plugin, request)
assert isinstance(parser, GtestParser)
assert parser.test_name == "Suite1.test_foo"
def test_discover_gtest_tests_no_product(monkeypatch):
"""Returns empty list when pytest.product is not set."""
monkeypatch.delattr(pytest, "product", raising=False)
result = _discover_gtest_tests("bin", None)
assert result == []
def test_discover_gtest_tests_returns_items(monkeypatch):
"""Returns discovered TestItems from the gtest binary."""
core = _make_core(output=_GTEST_OUTPUT)
product_mock = SimpleNamespace(core=lambda _: core)
monkeypatch.setattr(pytest, "product", product_mock, raising=False)
result = _discover_gtest_tests("gtest_bin", None)
assert len(result) == 2
assert result[0].name == "Suite1.test_foo"
def test_discover_gtest_tests_with_filter(monkeypatch):
"""fnmatch filter limits returned items."""
core = _make_core(output=_GTEST_OUTPUT)
product_mock = SimpleNamespace(core=lambda _: core)
monkeypatch.setattr(pytest, "product", product_mock, raising=False)
result = _discover_gtest_tests("gtest_bin", "Suite1.test_foo")
assert len(result) == 1
assert result[0].name == "Suite1.test_foo"