Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ When the flag **--adi-hw-map** is used the provided map from [plugin itself is u

If the flag **--hw=<hardware name>** is used the hardware map is ignored and the provided URI is defined as that hardware. This is handy if you do not want to create a custom map or are debugging. Note that **--hw** is only applicable when **--uri** is also used.

A hardware entry may also carry a `blacklist:` section used to exclude specific devices, channels, or attributes from **--iio-coverage** tracking. See [Blacklisting devices, channels, and attributes](#blacklisting-devices-channels-and-attributes).
A hardware entry may also carry an `ignore:` section used to exclude specific devices, channels, or attributes from **--iio-coverage** tracking. See [Ignoring devices, channels, and attributes](#ignoring-devices-channels-and-attributes).

## Telemetry

Expand Down Expand Up @@ -106,14 +106,14 @@ Both land in the directory named by **--iio-coverage-folder** (default
per-context attribute map to stdout, or **--iio-coverage-debug-props** to
include debug attributes in the tally.

### Blacklisting devices, channels, and attributes
### Ignoring devices, channels, and attributes

Some attributes are noise for coverage purposes — diagnostic devices, write-only
or unsupported attributes, or whole families no test could reasonably exercise.
These drag down the reported percentage without adding signal. To exclude them,
add a `blacklist:` section to the relevant hardware entry in the
add an `ignore:` section to the relevant hardware entry in the
[hardware map](#hardware-maps) (works with both **--adi-hw-map** and
**--custom-hw-map**). Blacklisted items are removed entirely: they are not
**--custom-hw-map**). Ignored items are removed entirely: they are not
counted when accessed and do not appear in the coverage denominator.

``` yaml
Expand All @@ -122,7 +122,7 @@ pluto:
- cf-ad9361-lpc,2
- emulate:
- filename: pluto.xml
- blacklist:
- ignore:
devices: # whole devices (all channels/attrs)
- xadc
- "cf-*" # glob: every capture device
Expand All @@ -147,6 +147,6 @@ pluto:
Every string field is matched as a case-sensitive glob (Python's
`fnmatch.fnmatchcase`), so `*` and `?` wildcards are supported, and an omitted
optional field matches anything. Debug attributes (**--iio-coverage-debug-props**)
honor whole-`devices` blacklisting only.
honor whole-`devices` ignoring only.
Comment on lines 149 to +150

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (typo): Consider rephrasing the final clause for clarity ("ignoring only" reads awkwardly).

The phrase “honor whole-devices ignoring only” reads like a leftover from the previous “blacklisting” wording. Consider something like “honor only whole-devices entries in the ignore section” or “only honor whole-devices ignore entries” to make the intent explicit.

Suggested change
optional field matches anything. Debug attributes (**--iio-coverage-debug-props**)
honor whole-`devices` blacklisting only.
honor whole-`devices` ignoring only.
optional field matches anything. Debug attributes (**--iio-coverage-debug-props**)
only honor whole-`devices` entries in the ignore section.


For a worked example see [Scenario: attribute coverage](fixtures.md#scenario-attribute-coverage).
41 changes: 20 additions & 21 deletions pytest_libiio/coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ def _match(pattern, value):
return fnmatch.fnmatchcase(value, pattern)


class Blacklist:
class IgnoreList:
"""Match devices, channels, and attributes to exclude from coverage.

Built from a per-hardware ``blacklist`` mapping in the hardware map YAML::
Built from a per-hardware ``ignore`` mapping in the hardware map YAML::

blacklist:
ignore:
devices: [xadc, "cf-*"]
channels:
- {device: ad9361-phy, id: voltage0}
Expand All @@ -38,13 +38,13 @@ def __init__(self, spec):
self._channels = list(spec.get("channels") or [])
self._attributes = list(spec.get("attributes") or [])

def device_blacklisted(self, device):
def device_ignored(self, device):
"""True if the whole device is excluded."""
return any(_match(pat, device) for pat in self._devices)

def channel_blacklisted(self, device, channel, direction):
def channel_ignored(self, device, channel, direction):
"""True if the whole channel (and all its attrs) is excluded."""
if self.device_blacklisted(device):
if self.device_ignored(device):
return True
for entry in self._channels:
if (
Expand All @@ -55,14 +55,14 @@ def channel_blacklisted(self, device, channel, direction):
return True
return False

def attr_blacklisted(self, device, channel, attr, direction):
def attr_ignored(self, device, channel, attr, direction):
"""True if a single attribute is excluded.

``channel`` is ``None`` for device-level attributes; otherwise the
attribute belongs to the named channel.
"""
if channel is None:
if self.device_blacklisted(device):
if self.device_ignored(device):
return True
for entry in self._attributes:
if "channel" in entry:
Expand All @@ -73,7 +73,7 @@ def attr_blacklisted(self, device, channel, attr, direction):
return True
return False

if self.channel_blacklisted(device, channel, direction):
if self.channel_ignored(device, channel, direction):
return True
for entry in self._attributes:
if "channel" not in entry:
Expand All @@ -96,9 +96,9 @@ def __init__(self):
self.track_context_props = False
self.track_debug_props = False
self.results_folder = "iio_coverage_results"
# Per-hardware blacklist specs, keyed by hardware name. Populated from
# Per-hardware ignore specs, keyed by hardware name. Populated from
# the hardware map; consulted in add_instance.
self.blacklists = {}
self.ignores = {}

def do_monkey_patch(self):
"""Apply monkey patch to iio.py."""
Expand All @@ -120,7 +120,7 @@ def add_instance(self, name, uri):
uri,
self.track_context_props,
self.track_debug_props,
blacklist=self.blacklists.get(name),
ignore=self.ignores.get(name),
)
else:
raise Exception(f"{name} already in tracker list")
Expand All @@ -144,7 +144,7 @@ def __init__(
track_context_props=False,
track_debug_props=False,
results_folder=None,
blacklist=None,
ignore=None,
):
self.name = name
self.context_attr_reads_writes = {}
Expand All @@ -156,7 +156,7 @@ def __init__(
self.track_context_props = track_context_props
self.track_debug_props = track_debug_props
self.results_folder = results_folder or "iio_coverage_results"
self.blacklist = Blacklist(blacklist) if blacklist else None
self.ignore = IgnoreList(ignore) if ignore else None
self.build_context_map()

def reset(self):
Expand All @@ -169,24 +169,24 @@ def reset(self):
def build_context_map(self):
"""Build a map of context attributes.

Entries excluded by ``self.blacklist`` are omitted entirely so they
Entries excluded by ``self.ignore`` are omitted entirely so they
never count toward coverage denominators.
"""
bl = self.blacklist
ig = self.ignore
if self.track_context_props:
self.context_attr_reads_writes = {attr: 0 for attr in self.ctx.attrs}
for dev in self.ctx.devices:
if bl and bl.device_blacklisted(dev.name):
if ig and ig.device_ignored(dev.name):
continue
self.device_attr_reads_writes[dev.name] = {
attr: 0
for attr in dev.attrs
if not (bl and bl.attr_blacklisted(dev.name, None, attr, None))
if not (ig and ig.attr_ignored(dev.name, None, attr, None))
}
for inout in ["input", "output"]:
for channel in dev.channels:
inout = "output" if channel.output else "input"
if bl and bl.channel_blacklisted(dev.name, channel.id, inout):
if ig and ig.channel_ignored(dev.name, channel.id, inout):
continue
if dev.name not in self.channel_attr_reads_writes:
self.channel_attr_reads_writes[dev.name] = {}
Comment on lines 186 to 192

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion: The for inout in ["input", "output"] loop is redundant because inout is immediately overwritten.

Because inout is reassigned from channel.output on every iteration, the outer loop over ["input", "output"] has no effect and just duplicates the work. Remove that loop and compute inout once per channel from channel.output to simplify the logic and avoid double processing.

Expand All @@ -196,8 +196,7 @@ def build_context_map(self):
attr: 0
for attr in channel.attrs
if not (
bl
and bl.attr_blacklisted(dev.name, channel.id, attr, inout)
ig and ig.attr_ignored(dev.name, channel.id, attr, inout)
)
}
if self.track_debug_props:
Expand Down
16 changes: 6 additions & 10 deletions pytest_libiio/mkpatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,15 @@ def _read(self):
device_name = name_raw.decode("ascii") if name_raw is not None else None

tracker = _get_tracker()
bl = getattr(tracker, "blacklist", None)
ig = getattr(tracker, "ignore", None)
if channel_name and device_name:
inout = "output" if output else "input"
if not (
bl and bl.attr_blacklisted(device_name, channel_name, attr_name, inout)
):
if not (ig and ig.attr_ignored(device_name, channel_name, attr_name, inout)):
tracker.channel_attr_reads_writes[device_name][inout][channel_name][
attr_name
] += 1
elif device_name:
if not (bl and bl.attr_blacklisted(device_name, None, attr_name, None)):
if not (ig and ig.attr_ignored(device_name, None, attr_name, None)):
tracker.device_attr_reads_writes[device_name][attr_name] += 1
else:
tracker.context_attr_reads_writes[attr_name] += 1
Expand Down Expand Up @@ -79,17 +77,15 @@ def _write(self, value):
device_name = name_raw.decode("ascii") if name_raw is not None else None

tracker = _get_tracker()
bl = getattr(tracker, "blacklist", None)
ig = getattr(tracker, "ignore", None)
if channel_name and device_name:
inout = "output" if output else "input"
if not (
bl and bl.attr_blacklisted(device_name, channel_name, attr_name, inout)
):
if not (ig and ig.attr_ignored(device_name, channel_name, attr_name, inout)):
tracker.channel_attr_reads_writes[device_name][inout][channel_name][
attr_name
] += 1
elif device_name:
if not (bl and bl.attr_blacklisted(device_name, None, attr_name, None)):
if not (ig and ig.attr_ignored(device_name, None, attr_name, None)):
tracker.device_attr_reads_writes[device_name][attr_name] += 1
else:
tracker.context_attr_reads_writes[attr_name] += 1
Expand Down
20 changes: 10 additions & 10 deletions pytest_libiio/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,23 +132,23 @@ def get_filename(map, hw):
return fn, dd


def extract_blacklists(map):
"""Return ``{hardware_name: blacklist_spec}`` for entries that define one.
def extract_ignores(map):
"""Return ``{hardware_name: ignore_spec}`` for entries that define one.

The blacklist is a ``blacklist:`` dict item inside a hardware entry's list,
The ignore spec is an ``ignore:`` dict item inside a hardware entry's list,
alongside ``emulate:``/``ctx_attr:``. Used to exclude devices, channels, or
attributes from ``--iio-coverage`` tracking.
"""
blacklists = {}
ignores = {}
if not map:
return blacklists
return ignores
for hw, items in map.items():
if not isinstance(items, list):
continue
for item in items:
if isinstance(item, dict) and "blacklist" in item:
blacklists[hw] = item["blacklist"]
return blacklists
if isinstance(item, dict) and "ignore" in item:
ignores[hw] = item["ignore"]
return ignores


def handle_iio_emu(ctx, request, _iio_emu):
Expand Down Expand Up @@ -344,12 +344,12 @@ class Object(object):
tracker.track_debug_props = bool(
session.config.getoption("--iio-coverage-debug-props")
)
# Load per-hardware coverage blacklists from the hardware map, if one is
# Load per-hardware coverage ignores from the hardware map, if one is
# in use. get_hw_map only reads config options, so the session stands in
# for a request here.
hw_map = get_hw_map(session)
if hw_map:
tracker.blacklists = extract_blacklists(hw_map)
tracker.ignores = extract_ignores(hw_map)
print("IIO coverage tracking enabled")
else:
session.config.pytest_libiio = Object()
Expand Down
Loading