166 lines
7 KiB
Java
166 lines
7 KiB
Java
package unit;
|
|
|
|
import java.util.*;
|
|
|
|
/**
|
|
* Unit test for ros2-0002: NodeParameters::list_parameters() O(P²) → O(P)
|
|
*
|
|
* Simulates prefix deduplication in NodeParameters::list_parameters():
|
|
* Defective: std::find on result.prefixes (grows to P) inside loop over P parameters
|
|
* Fixed: unordered_set deduplication O(1) insert
|
|
*
|
|
* Compile: javac -d . ListParametersAlgorithm.java
|
|
* Run: java -ea unit.ListParametersAlgorithm
|
|
*/
|
|
public class ListParametersAlgorithm {
|
|
|
|
static class Result {
|
|
final List<String> names = new ArrayList<>();
|
|
final List<String> prefixes = new ArrayList<>();
|
|
}
|
|
|
|
// ── defective implementation ──────────────────────────────────────────────
|
|
|
|
static Result defectiveListParameters(List<String> parameterNames) {
|
|
Result result = new Result();
|
|
final char separator = '.';
|
|
|
|
for (String name : parameterNames) {
|
|
result.names.add(name);
|
|
int lastSep = name.lastIndexOf(separator);
|
|
if (lastSep != -1) {
|
|
String prefix = name.substring(0, lastSep);
|
|
// O(P) linear scan to deduplicate prefixes
|
|
boolean found = false;
|
|
for (String p : result.prefixes) {
|
|
if (p.equals(prefix)) { found = true; break; }
|
|
}
|
|
if (!found) result.prefixes.add(prefix);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// ── fixed implementation ──────────────────────────────────────────────────
|
|
|
|
static Result fixedListParameters(List<String> parameterNames) {
|
|
Result result = new Result();
|
|
final char separator = '.';
|
|
Set<String> seenPrefixes = new HashSet<>();
|
|
|
|
for (String name : parameterNames) {
|
|
result.names.add(name);
|
|
int lastSep = name.lastIndexOf(separator);
|
|
if (lastSep != -1) {
|
|
String prefix = name.substring(0, lastSep);
|
|
if (seenPrefixes.add(prefix)) { // O(1) insert + dedup
|
|
result.prefixes.add(prefix);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// ── tests ─────────────────────────────────────────────────────────────────
|
|
|
|
static int pass = 0, total = 0;
|
|
|
|
static void assertTrue(String name, boolean cond) {
|
|
total++;
|
|
if (cond) { pass++; System.out.println("PASS " + name); }
|
|
else System.out.println("FAIL " + name);
|
|
}
|
|
|
|
// Build nav2-style parameter names: namespace.group.param
|
|
static List<String> buildNav2Params(int namespaces, int groups, int paramsPerGroup) {
|
|
List<String> names = new ArrayList<>();
|
|
for (int ns = 0; ns < namespaces; ns++) {
|
|
for (int g = 0; g < groups; g++) {
|
|
for (int p = 0; p < paramsPerGroup; p++) {
|
|
names.add("ns" + ns + ".group" + g + ".param" + p);
|
|
}
|
|
}
|
|
}
|
|
return names;
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
// Test 1: empty parameters → empty result
|
|
{
|
|
Result d = defectiveListParameters(Collections.emptyList());
|
|
Result f = fixedListParameters(Collections.emptyList());
|
|
assertTrue("empty-defective", d.names.isEmpty() && d.prefixes.isEmpty());
|
|
assertTrue("empty-fixed", f.names.isEmpty() && f.prefixes.isEmpty());
|
|
}
|
|
|
|
// Test 2: parameters with no separator → no prefixes
|
|
{
|
|
List<String> params = Arrays.asList("foo", "bar", "baz");
|
|
Result d = defectiveListParameters(params);
|
|
Result f = fixedListParameters(params);
|
|
assertTrue("no-sep-defective", d.prefixes.isEmpty() && d.names.size() == 3);
|
|
assertTrue("no-sep-fixed", f.prefixes.isEmpty() && f.names.size() == 3);
|
|
}
|
|
|
|
// Test 3: all same prefix → only one prefix entry
|
|
{
|
|
List<String> params = Arrays.asList("nav2.speed", "nav2.timeout", "nav2.max_vel");
|
|
Result d = defectiveListParameters(params);
|
|
Result f = fixedListParameters(params);
|
|
assertTrue("same-prefix-dedup-defective", d.prefixes.size() == 1 && d.prefixes.get(0).equals("nav2"));
|
|
assertTrue("same-prefix-dedup-fixed", f.prefixes.size() == 1 && f.prefixes.get(0).equals("nav2"));
|
|
}
|
|
|
|
// Test 4: multiple distinct prefixes
|
|
{
|
|
List<String> params = Arrays.asList(
|
|
"nav2.speed", "nav2.timeout",
|
|
"moveit.planning_time", "moveit.max_vel",
|
|
"slam.resolution");
|
|
Result d = defectiveListParameters(params);
|
|
Result f = fixedListParameters(params);
|
|
assertTrue("multi-prefix-defective", d.prefixes.size() == 3);
|
|
assertTrue("multi-prefix-fixed", f.prefixes.size() == 3);
|
|
assertTrue("multi-prefix-names-match", d.names.equals(f.names));
|
|
// prefixes sets must be equal (order may differ)
|
|
assertTrue("multi-prefix-sets-match",
|
|
new HashSet<>(d.prefixes).equals(new HashSet<>(f.prefixes)));
|
|
}
|
|
|
|
// Test 5: nav2-style — 3 namespaces, 10 groups, 5 params each = 150 params, 30 unique prefixes
|
|
{
|
|
List<String> params = buildNav2Params(3, 10, 5);
|
|
Result d = defectiveListParameters(params);
|
|
Result f = fixedListParameters(params);
|
|
assertTrue("nav2-names-count-defective", d.names.size() == 150);
|
|
assertTrue("nav2-names-count-fixed", f.names.size() == 150);
|
|
// Each ns.group is a unique "ns_X.group_Y" prefix → 3*10 = 30 unique prefixes
|
|
assertTrue("nav2-prefix-count-defective", d.prefixes.size() == 30);
|
|
assertTrue("nav2-prefix-count-fixed", f.prefixes.size() == 30);
|
|
assertTrue("nav2-prefix-sets-match",
|
|
new HashSet<>(d.prefixes).equals(new HashSet<>(f.prefixes)));
|
|
}
|
|
|
|
// Test 6: performance at P=500 (500 unique prefixes worst case)
|
|
{
|
|
List<String> params = new ArrayList<>();
|
|
for (int i = 0; i < 500; i++) params.add("group" + i + ".param" + i);
|
|
|
|
long t0 = System.nanoTime();
|
|
for (int r = 0; r < 200; r++) defectiveListParameters(params);
|
|
long tDef = System.nanoTime() - t0;
|
|
|
|
t0 = System.nanoTime();
|
|
for (int r = 0; r < 200; r++) fixedListParameters(params);
|
|
long tFix = System.nanoTime() - t0;
|
|
|
|
double ratio = (double) tDef / tFix;
|
|
System.out.printf(" Perf P=500 unique: defective=%.1fms fixed=%.1fms ratio=%.1fx%n",
|
|
tDef / 1e6, tFix / 1e6, ratio);
|
|
assertTrue("perf-speedup", ratio > 3.0);
|
|
}
|
|
|
|
System.out.println(pass + "/" + total + " PASS");
|
|
assert pass == total : pass + "/" + total + " passed";
|
|
}
|
|
}
|