Skip to content
Draft
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
34 changes: 34 additions & 0 deletions src/main/java/de/rub/nds/scanner/core/execution/Scanner.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,25 @@ public abstract class Scanner<
* @param executorConfig The executor configuration to use.
*/
public Scanner(ExecutorConfig executorConfig) {
this(executorConfig, (ProbeProgressCallback<ReportT, StateT>) null);
}

/**
* Creates a new scanner instance with a progress callback.
*
* @param executorConfig The executor configuration to use.
* @param progressCallback the callback to invoke on probe completion, or null for no callbacks
*/
public Scanner(
ExecutorConfig executorConfig,
ProbeProgressCallback<ReportT, StateT> progressCallback) {
this.executorConfig = executorConfig;
probeList = new LinkedList<>();
afterList = new LinkedList<>();
fillProbeListsAtScanStart = true;
if (progressCallback != null) {
this.progressCallback = progressCallback;
}
}

/**
Expand All @@ -73,10 +88,29 @@ public Scanner(ExecutorConfig executorConfig) {
*/
public Scanner(
ExecutorConfig executorConfig, List<ProbeT> probeList, List<AfterProbeT> afterList) {
this(executorConfig, probeList, afterList, null);
}

/**
* Creates a new scanner instance with a progress callback.
*
* @param executorConfig The executor configuration to use.
* @param probeList The list of probes to execute.
* @param afterList The list of after probes to execute.
* @param progressCallback the callback to invoke on probe completion, or null for no callbacks
*/
public Scanner(
ExecutorConfig executorConfig,
List<ProbeT> probeList,
List<AfterProbeT> afterList,
ProbeProgressCallback<ReportT, StateT> progressCallback) {
this.executorConfig = executorConfig;
this.probeList = new LinkedList<>(probeList);
this.afterList = new LinkedList<>(afterList);
fillProbeListsAtScanStart = false;
if (progressCallback != null) {
this.progressCallback = progressCallback;
}
}

/**
Expand Down
85 changes: 85 additions & 0 deletions src/test/java/de/rub/nds/scanner/core/execution/ScannerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,25 @@ static class TestScanner extends Scanner<TestReport, TestProbe, TestAfterProbe,
super(config);
}

TestScanner(
ExecutorConfig config,
ProbeProgressCallback<TestReport, TestState> progressCallback) {
super(config, progressCallback);
}

TestScanner(
ExecutorConfig config, List<TestProbe> probeList, List<TestAfterProbe> afterList) {
super(config, probeList, afterList);
}

TestScanner(
ExecutorConfig config,
List<TestProbe> probeList,
List<TestAfterProbe> afterList,
ProbeProgressCallback<TestReport, TestState> progressCallback) {
super(config, probeList, afterList, progressCallback);
}

@Override
public void close() {
// Implementation for AutoCloseable
Expand Down Expand Up @@ -506,4 +520,75 @@ public void testProgressCallbackReportsCorrectProbeCount() {
// Verify callback was invoked for each probe
assertEquals(expectedProbeCount, completedCounts.size());
}

// ---- Constructor-based progress callback tests ----

@Test
public void testConstructorWithCallbackIsInvoked() {
List<TestProbe> probeList = new ArrayList<>();
probeList.add(new TestProbe(new TestProbeType("probe1")));
probeList.add(new TestProbe(new TestProbeType("probe2")));

List<TestAfterProbe> afterList = new ArrayList<>();

final int[] callbackInvocations = {0};
ProbeProgressCallback<TestReport, TestState> callback =
(probe, report, completedProbes, totalProbes) -> callbackInvocations[0]++;

try (TestScanner scanner =
new TestScanner(executorConfig, probeList, afterList, callback)) {
scanner.scan();
}

assertEquals(2, callbackInvocations[0], "Constructor callback should be invoked per probe");
}

@Test
public void testConstructorWithNullCallbackScansSuccessfully() {
try (TestScanner scanner = new TestScanner(executorConfig, null)) {
TestReport report = scanner.scan();
assertNotNull(report);
}
}

@Test
public void testDefaultConstructorCallbackIsInvokedViaFillProbes() {
final int[] callbackInvocations = {0};
ProbeProgressCallback<TestReport, TestState> callback =
(probe, report, completedProbes, totalProbes) -> callbackInvocations[0]++;

try (TestScanner scanner = new TestScanner(executorConfig, callback)) {
// fillProbeLists will be called; no probes added so callback won't fire
TestReport report = scanner.scan();
assertNotNull(report);
assertTrue(scanner.isFillProbesCalled());
}

assertEquals(0, callbackInvocations[0], "No probes registered so callback should not fire");
}

@Test
public void testConstructorCallbackReportsCorrectProbeCount() {
List<TestProbe> probeList = new ArrayList<>();
int expectedProbeCount = 3;
for (int i = 0; i < expectedProbeCount; i++) {
probeList.add(new TestProbe(new TestProbeType("probe" + i)));
}

List<TestAfterProbe> afterList = new ArrayList<>();

final List<Integer> completedCounts = new ArrayList<>();
ProbeProgressCallback<TestReport, TestState> callback =
(probe, report, completedProbes, totalProbes) -> {
assertEquals(expectedProbeCount, totalProbes);
completedCounts.add(completedProbes);
};

try (TestScanner scanner =
new TestScanner(executorConfig, probeList, afterList, callback)) {
scanner.scan();
}

assertEquals(expectedProbeCount, completedCounts.size());
}
}