Jan Herrington

15 papers Misc 1Journal 10Unranked 3
YearRankTypeTitle / Venue / Authors
2017 J jnl
J. Educ. Technol. Soc.
Ildeniz Ozverir, Ulker Vanci Osam, Jan Herrington
2016 J jnl
Br. J. Educ. Technol.
Ildeniz Ozverir, Jan Herrington, Ulker Vanci Osam
2016 J jnl
Br. J. Educ. Technol.
James L. Vesper, Ümit Kartoglu, Jan Herrington, Thomas C. Reeves
2016 J jnl
J. Educ. Technol. Soc.
Pauline Roberts, Dorit Maor, Jan Herrington
2013 J jnl
Br. J. Educ. Technol.
Jan Herrington, Jenni Parker
2013 J jnl
Br. J. Educ. Technol.
Vivienne Bozalek, Daniela Gachago, Lucy Alexander, Kathy Watters, Denise Wood, Eunice Ivala, Jan Herrington
2009 Misc conf
IDC
Irina Verenikina, Jan Herrington
2009 ch.
Learning Network Services for Professional Development
Jan Herrington, Marcus Specht, Gwyn Brickell, Barry Harper
2007 J jnl
J. Comput. High. Educ.
Jan Herrington, Thomas C. Reeves, Ron Oliver
2005 J jnl
J. Comput. High. Educ.
Thomas C. Reeves, Jan Herrington, Ron Oliver
2004 J jnl
J. Comput. High. Educ.
Jan Herrington, Thomas C. Reeves, Ron Oliver, Younghee Woo
2003 J jnl
Interact. Learn. Environ.
Ron Oliver, Jan Herrington
2003 conf
Quality Education @ a Distance
Ron Oliver, Jan Herrington
2002 conf
ASCILITE
Jan Herrington, Ron Oliver, Thomas C. Reeves
2002 conf
ASCILITE
Anthony Herrington, Jan Herrington, Arshad Omari
tests/unit/test_decompile_strings.py
← Index tests/unit/test_decompile_strings.py python
"""Unit tests for bninja/analysis/strings.py — StringAnalysis."""
import pytest
import math
from unittest.mock import MagicMock


# StringAnalysis has no binaryninja imports, just collections and math
from redb.extractors.decompiler.bninja.analysis.strings import StringAnalysis


# ============================================================================
# Helper mocks
# ============================================================================

class MockStringEntry:
    """Mock for a Binary Ninja string reference."""
    def __init__(self, value, raw=None, start=0, length=0, type_name="Utf8String"):
        self.value = value
        self.raw = raw if raw is not None else (value.encode("utf-8") if isinstance(value, str) else value)
        self.start = start
        self.length = length if length else len(self.raw)
        self.type = MagicMock()
        self.type.name = type_name


class MockBinaryView:
    """Mock binary view with a strings list."""
    def __init__(self, strings=None):
        self.strings = strings or []


# ============================================================================
# 6a. StringAnalysis
# ============================================================================


class TestStringAnalysisEntropy:
    def setup_method(self):
        self.sa = StringAnalysis(bv=MockBinaryView(), functions=[])

    def test_entropy_empty_string(self):
        assert self.sa.entropy("") == 0.0

    def test_entropy_single_char(self):
        assert self.sa.entropy("aaaa") == 0.0

    def test_entropy_uniform_distribution(self):
        # "abcd" -> 4 unique chars, each p=1/4, entropy = log2(4) = 2.0
        result = self.sa.entropy("abcd")
        assert result == pytest.approx(2.0)

    def test_entropy_binary_string(self):
        # "ab" -> 2 unique chars, each p=1/2, entropy = log2(2) = 1.0
        result = self.sa.entropy("ab")
        assert result == pytest.approx(1.0)


class TestStringAnalysisAnalyze:
    def test_analyze_deduplication(self):
        """Duplicate (string, encoding) pairs -> only first kept."""
        entries = [
            MockStringEntry("hello", start=100, type_name="Utf8String"),
            MockStringEntry("hello", start=200, type_name="Utf8String"),
        ]
        bv = MockBinaryView(strings=entries)
        sa = StringAnalysis(bv=bv, functions=[])
        result = sa.analyze()
        assert len(result) == 1
        assert result[0]["string_offset"] == 100

    def test_analyze_sorted_by_address(self):
        """First occurrence (lowest offset) is the one kept."""
        entries = [
            MockStringEntry("world", start=500, type_name="Utf8String"),
            MockStringEntry("world", start=100, type_name="Utf8String"),
        ]
        bv = MockBinaryView(strings=entries)
        sa = StringAnalysis(bv=bv, functions=[])
        result = sa.analyze()
        assert len(result) == 1
        # The analyze() sorts by start, so 100 comes first
        assert result[0]["string_offset"] == 100

    def test_analyze_empty_bv(self):
        bv = MockBinaryView(strings=[])
        sa = StringAnalysis(bv=bv, functions=[])
        result = sa.analyze()
        assert result == []

    def test_analyze_output_schema(self):
        entries = [MockStringEntry("test_string", start=0, type_name="Utf8String")]
        bv = MockBinaryView(strings=entries)
        sa = StringAnalysis(bv=bv, functions=[])
        result = sa.analyze()
        assert len(result) == 1
        r = result[0]
        required_keys = [
            "string",
            "string_raw",
            "string_encoding",
            "string_offset",
            "string_length",
            "string_raw_length",
            "string_entropy",
        ]
        for key in required_keys:
            assert key in r, f"Missing key: {key}"