164 lines
6.6 KiB
Java
164 lines
6.6 KiB
Java
package unit;
|
|
|
|
import java.util.*;
|
|
|
|
/**
|
|
* pulsar-0002: JavaInstanceRunnable.setupConfig() — List.contains() in config key validation loop.
|
|
*
|
|
* allFields is a List<String> from BeanPropertiesReader.getBeanProperties().
|
|
* For each config key, .contains() is O(F). With K keys, total is O(K * F).
|
|
* Fix: convert allFields to HashSet<String> before the loop.
|
|
*/
|
|
public class PulsarJavaInstanceRunnableTest {
|
|
|
|
// Slow path: List<String> allFields with .contains() per key
|
|
static Object[] slowValidateConfig(List<String> allFields, Set<String> configKeys) {
|
|
long ops = 0;
|
|
List<String> invalidKeys = new ArrayList<>();
|
|
for (String s : configKeys) {
|
|
// allFields.contains(s): O(F) linear scan
|
|
boolean found = false;
|
|
for (String f : allFields) {
|
|
ops++;
|
|
if (f.equals(s)) { found = true; break; }
|
|
}
|
|
if (!found) {
|
|
invalidKeys.add(s);
|
|
}
|
|
}
|
|
return new Object[]{invalidKeys, ops};
|
|
}
|
|
|
|
// Fast path: HashSet<String> for O(1) lookup
|
|
static Object[] fastValidateConfig(List<String> allFields, Set<String> configKeys) {
|
|
Set<String> fieldSet = new HashSet<>(allFields);
|
|
long ops = 0;
|
|
List<String> invalidKeys = new ArrayList<>();
|
|
for (String s : configKeys) {
|
|
ops++; // O(1) HashSet.contains
|
|
if (!fieldSet.contains(s)) {
|
|
invalidKeys.add(s);
|
|
}
|
|
}
|
|
return new Object[]{invalidKeys, ops};
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
int passed = 0;
|
|
int total = 0;
|
|
|
|
// Test 1: correctness — no invalid keys
|
|
{
|
|
total++;
|
|
List<String> allFields = Arrays.asList("host", "port", "timeout", "retries", "user");
|
|
Set<String> configKeys = new HashSet<>(Arrays.asList("host", "port", "timeout"));
|
|
|
|
@SuppressWarnings("unchecked")
|
|
List<String> slowInvalid = (List<String>) slowValidateConfig(allFields, configKeys)[0];
|
|
@SuppressWarnings("unchecked")
|
|
List<String> fastInvalid = (List<String>) fastValidateConfig(allFields, configKeys)[0];
|
|
|
|
assert slowInvalid.isEmpty() : "No invalid keys expected, slow found: " + slowInvalid;
|
|
assert fastInvalid.isEmpty() : "No invalid keys expected, fast found: " + fastInvalid;
|
|
System.out.println(" Test 1 PASS: no invalid keys");
|
|
passed++;
|
|
}
|
|
|
|
// Test 2: correctness — some invalid keys
|
|
{
|
|
total++;
|
|
List<String> allFields = Arrays.asList("host", "port", "timeout");
|
|
Set<String> configKeys = new HashSet<>(Arrays.asList("host", "port", "badKey", "anotherBad"));
|
|
|
|
@SuppressWarnings("unchecked")
|
|
List<String> slowInvalid = (List<String>) slowValidateConfig(allFields, configKeys)[0];
|
|
@SuppressWarnings("unchecked")
|
|
List<String> fastInvalid = (List<String>) fastValidateConfig(allFields, configKeys)[0];
|
|
|
|
Set<String> slowSet = new HashSet<>(slowInvalid);
|
|
Set<String> fastSet = new HashSet<>(fastInvalid);
|
|
|
|
assert slowSet.equals(fastSet)
|
|
: "Slow and fast must find same invalid keys. slow=" + slowSet + " fast=" + fastSet;
|
|
assert slowSet.contains("badKey") : "badKey must be invalid";
|
|
assert slowSet.contains("anotherBad") : "anotherBad must be invalid";
|
|
assert !slowSet.contains("host") : "host is valid, must not be invalid";
|
|
System.out.println(" Test 2 PASS: invalid keys=" + slowSet);
|
|
passed++;
|
|
}
|
|
|
|
// Test 3: op count — slow O(K*F) vs fast O(K)
|
|
{
|
|
total++;
|
|
int F = 150; // fields in config class
|
|
int K = 100; // config keys
|
|
|
|
List<String> allFields = new ArrayList<>();
|
|
for (int i = 0; i < F; i++) allFields.add("field" + i);
|
|
|
|
// Config keys: half valid, half invalid
|
|
Set<String> configKeys = new HashSet<>();
|
|
for (int i = 0; i < K / 2; i++) configKeys.add("field" + i); // valid
|
|
for (int i = 0; i < K / 2; i++) configKeys.add("unknown-key-" + i); // invalid
|
|
|
|
Object[] slowOut = slowValidateConfig(allFields, configKeys);
|
|
Object[] fastOut = fastValidateConfig(allFields, configKeys);
|
|
|
|
@SuppressWarnings("unchecked")
|
|
Set<String> slowInvalidSet = new HashSet<>((List<String>) slowOut[0]);
|
|
@SuppressWarnings("unchecked")
|
|
Set<String> fastInvalidSet = new HashSet<>((List<String>) fastOut[0]);
|
|
|
|
assert slowInvalidSet.equals(fastInvalidSet)
|
|
: "Slow and fast must agree on invalid keys";
|
|
|
|
long slowOps = (Long) slowOut[1];
|
|
long fastOps = (Long) fastOut[1];
|
|
|
|
assert slowOps > fastOps
|
|
: "Slow must do more ops: slowOps=" + slowOps + " fastOps=" + fastOps;
|
|
assert fastOps == K
|
|
: "Fast must make exactly K=" + K + " ops, got " + fastOps;
|
|
|
|
long speedup = slowOps / fastOps;
|
|
System.out.println(" Test 3 PASS: slowOps=" + slowOps + " fastOps=" + fastOps
|
|
+ " speedup=" + speedup + "x (F=" + F + " K=" + K + ")");
|
|
passed++;
|
|
}
|
|
|
|
// Test 4: worst-case O(K*F) — all config keys invalid (scan all F every time)
|
|
{
|
|
total++;
|
|
int F = 100;
|
|
int K = 80;
|
|
List<String> allFields = new ArrayList<>();
|
|
for (int i = 0; i < F; i++) allFields.add("field" + i);
|
|
|
|
Set<String> configKeys = new HashSet<>();
|
|
for (int i = 0; i < K; i++) configKeys.add("invalid-key-" + i);
|
|
|
|
long expectedSlowOps = (long) K * F; // every key scans all F fields
|
|
long actualSlowOps = 0;
|
|
for (String key : configKeys) {
|
|
for (String f : allFields) {
|
|
actualSlowOps++;
|
|
if (f.equals(key)) break;
|
|
}
|
|
}
|
|
|
|
assert actualSlowOps == expectedSlowOps
|
|
: "Expected " + expectedSlowOps + " ops (K*F), got " + actualSlowOps;
|
|
|
|
long fastOps = K;
|
|
long speedup = actualSlowOps / fastOps;
|
|
|
|
assert speedup == F : "Speedup should equal F=" + F + ", got " + speedup;
|
|
System.out.println(" Test 4 PASS: O(K*F)=" + actualSlowOps + " vs O(K)=" + fastOps
|
|
+ " speedup=" + speedup + "x");
|
|
passed++;
|
|
}
|
|
|
|
System.out.println(passed + "/" + total + " PASS");
|
|
if (passed != total) System.exit(1);
|
|
}
|
|
}
|