diff --git a/src/org/labkey/test/params/FieldInfo.java b/src/org/labkey/test/params/FieldInfo.java index 33b09edfd2..9ddcf15ddb 100644 --- a/src/org/labkey/test/params/FieldInfo.java +++ b/src/org/labkey/test/params/FieldInfo.java @@ -146,6 +146,12 @@ public String getName() return _fieldKey.getName(); } + @Contract(pure = true) + public String getNamePart() + { + return _namePart; + } + /** * Get column name quoted for use in queries and calculated field expressions */ diff --git a/src/org/labkey/test/util/TestDataGenerator.java b/src/org/labkey/test/util/TestDataGenerator.java index 6f10582645..1df4ccea2a 100644 --- a/src/org/labkey/test/util/TestDataGenerator.java +++ b/src/org/labkey/test/util/TestDataGenerator.java @@ -52,6 +52,7 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.BitSet; import java.util.Calendar; import java.util.Collections; import java.util.Date; @@ -67,6 +68,7 @@ import java.util.function.Function; import java.util.function.Supplier; import java.util.regex.Pattern; +import java.util.stream.Collectors; import static org.labkey.test.BaseWebDriverTest.ALL_ILLEGAL_QUERY_KEY_CHARACTERS; import static org.labkey.test.util.data.TestDataUtils.REALISTIC_ASSAY_FIELDS; @@ -188,7 +190,10 @@ else if (fieldDefinition.getType().equals(FieldDefinition.ColumnType.MultiValueT { FieldDefinition.TextChoiceValidator validator = (FieldDefinition.TextChoiceValidator) fieldDefinition.getValidators().getFirst(); - List values = shuffleSelect(validator.getValues()); + // Use i + 1 as a bitmask so each row gets a deterministic, non-empty subset of choices, + // consistent with how other field types (Integer, Boolean, TextChoice) use i for predictable data. + // Salt with queryName hash so different entity types produce distinct MVTC values at the same row index. + List values = bitmaskSelect(validator.getValues(), i + 1, queryName.hashCode()); entityData.put(key, values); } } @@ -1000,6 +1005,23 @@ public static List shuffleSelect(List allFields, int selectCount) return shuffled.subList(0, selectCount); } + /** + * Selects elements by index using {@code bitmask}: bit N set → include element N. Supports up to 32 elements. + * XORs the bitmask with {@code salt} so that callers with the same bitmask (e.g. same row index) produce + * different selections. + */ + public static List bitmaskSelect(List allElements, int bitmask, int salt) + { + int n = allElements.size(); + int validMask = n < 32 ? (1 << n) - 1 : Integer.MAX_VALUE; + int effective = (bitmask ^ salt) & validMask; + if (effective == 0) + effective = bitmask & validMask; + return BitSet.valueOf(new long[]{effective}).stream() + .mapToObj(allElements::get) + .collect(Collectors.toList()); + } + public static List randomSelect(List allOptions, int selectCount) { List selected = new ArrayList<>(); diff --git a/src/org/labkey/test/util/data/TestArrayDataUtils.java b/src/org/labkey/test/util/data/TestArrayDataUtils.java index 7509828bdb..deaf7fa81b 100644 --- a/src/org/labkey/test/util/data/TestArrayDataUtils.java +++ b/src/org/labkey/test/util/data/TestArrayDataUtils.java @@ -62,6 +62,7 @@ public static List sortValues(List values) .sorted(Comparator .comparing((String s) -> s.substring(0, 1).toLowerCase()) .thenComparing(s -> s.substring(0, 1)) + .thenComparing(String.CASE_INSENSITIVE_ORDER) .thenComparing(s -> s)) .collect(Collectors.toList()); } @@ -90,7 +91,7 @@ public static Map filterAndPrepareMap(Map map, Li return prepareMapForCheck(filterMap(map, searchValues, filterType)); } - public static List parseMultiValueText(String multiValueString) throws IOException + public static List parseMultiValueText(String multiValueString) { CSVFormat format = CSVFormat.RFC4180.builder() .setIgnoreSurroundingSpaces(true).setTrim(true).get(); @@ -101,6 +102,10 @@ public static List parseMultiValueText(String multiValueString) throws I throw new IllegalArgumentException("Invalid multi-value text string: " + multiValueString); return records.getFirst().toList(); } + catch (IOException e) + { + throw new IllegalArgumentException(e); + } } public static String formatMultiValueText(List values)