mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-28 17:11:11 -09:00
Merge remote-tracking branch 'origin/GP-2557_ghidra1_ghizard_DemanglerAnonFuncDefNames_and_CategoryPath_changes'
This commit is contained in:
@@ -19,30 +19,28 @@ import ghidra.program.model.data.*;
|
|||||||
import ghidra.util.InvalidNameException;
|
import ghidra.util.InvalidNameException;
|
||||||
|
|
||||||
public class DataTypeNamingUtil {
|
public class DataTypeNamingUtil {
|
||||||
|
|
||||||
|
private static final String ANONYMOUS_FUNCTION_DEF_PREFIX = "_func";
|
||||||
|
|
||||||
private DataTypeNamingUtil() {
|
private DataTypeNamingUtil() {
|
||||||
// no construct
|
// no construct
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a simple mangled function definition name and apply it to the specified functionDefinition.
|
* Generate a simple mangled function definition name and apply it to the specified
|
||||||
|
* functionDefinition. Generated name will start with {@code _func}.
|
||||||
* @param functionDefinition function definition whose name should be set
|
* @param functionDefinition function definition whose name should be set
|
||||||
* @param namePrefix prefix to be applied to generated name. An underscore will separate this prefix from the
|
|
||||||
* remainder of the mangled name. If null specified a prefix of "_function" will be used.
|
|
||||||
* @return name applied to functionDefinition
|
* @return name applied to functionDefinition
|
||||||
* @throws IllegalArgumentException if generated name contains unsupported characters
|
* @throws IllegalArgumentException if generated name contains unsupported characters
|
||||||
*/
|
*/
|
||||||
public static String setMangledAnonymousFunctionName(
|
public static String setMangledAnonymousFunctionName(
|
||||||
FunctionDefinitionDataType functionDefinition, String namePrefix)
|
FunctionDefinitionDataType functionDefinition)
|
||||||
throws IllegalArgumentException {
|
throws IllegalArgumentException {
|
||||||
|
|
||||||
DataType returnType = functionDefinition.getReturnType();
|
DataType returnType = functionDefinition.getReturnType();
|
||||||
ParameterDefinition[] parameters = functionDefinition.getArguments();
|
ParameterDefinition[] parameters = functionDefinition.getArguments();
|
||||||
|
|
||||||
if (namePrefix == null) {
|
StringBuilder sb = new StringBuilder(ANONYMOUS_FUNCTION_DEF_PREFIX);
|
||||||
namePrefix = "_function";
|
|
||||||
}
|
|
||||||
StringBuilder sb = new StringBuilder(namePrefix);
|
|
||||||
|
|
||||||
GenericCallingConvention convention = functionDefinition.getGenericCallingConvention();
|
GenericCallingConvention convention = functionDefinition.getGenericCallingConvention();
|
||||||
if (convention != null && convention != GenericCallingConvention.unknown) {
|
if (convention != null && convention != GenericCallingConvention.unknown) {
|
||||||
|
|||||||
@@ -15,14 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.util.bin.format.dwarf4.next;
|
package ghidra.app.util.bin.format.dwarf4.next;
|
||||||
|
|
||||||
import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute.DW_AT_count;
|
import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute.*;
|
||||||
import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute.DW_AT_upper_bound;
|
import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag.*;
|
||||||
import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag.DW_TAG_subrange_type;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
@@ -311,7 +309,7 @@ public class DWARFDataTypeImporter {
|
|||||||
|
|
||||||
if (dni.isAnon() && mangleAnonFuncNames) {
|
if (dni.isAnon() && mangleAnonFuncNames) {
|
||||||
String mangledName =
|
String mangledName =
|
||||||
DataTypeNamingUtil.setMangledAnonymousFunctionName(funcDef, dni.getName());
|
DataTypeNamingUtil.setMangledAnonymousFunctionName(funcDef);
|
||||||
dni = dni.replaceName(mangledName, dni.getOriginalName());
|
dni = dni.replaceName(mangledName, dni.getOriginalName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import ghidra.app.util.DataTypeNamingUtil;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.symbol.Namespace;
|
import ghidra.program.model.symbol.Namespace;
|
||||||
|
|
||||||
@@ -314,7 +315,8 @@ public abstract class AbstractDemangledFunctionDefinitionDataType extends Demang
|
|||||||
@Override
|
@Override
|
||||||
public DataType getDataType(DataTypeManager dataTypeManager) {
|
public DataType getDataType(DataTypeManager dataTypeManager) {
|
||||||
|
|
||||||
FunctionDefinitionDataType fddt = new FunctionDefinitionDataType(getName());
|
FunctionDefinitionDataType fddt =
|
||||||
|
new FunctionDefinitionDataType(DEMANGLER_ANONYMOUS_FUNCTION_CATEGORY_PATH, getName());
|
||||||
|
|
||||||
if (returnType != null) {
|
if (returnType != null) {
|
||||||
fddt.setReturnType(returnType.getDataType(dataTypeManager));
|
fddt.setReturnType(returnType.getDataType(dataTypeManager));
|
||||||
@@ -322,6 +324,8 @@ public abstract class AbstractDemangledFunctionDefinitionDataType extends Demang
|
|||||||
|
|
||||||
setParameters(fddt, dataTypeManager);
|
setParameters(fddt, dataTypeManager);
|
||||||
|
|
||||||
|
DataTypeNamingUtil.setMangledAnonymousFunctionName(fddt);
|
||||||
|
|
||||||
DataType dt = DemangledDataType.findDataType(dataTypeManager, namespace, getName());
|
DataType dt = DemangledDataType.findDataType(dataTypeManager, namespace, getName());
|
||||||
if (dt == null || !(dt instanceof FunctionDefinitionDataType)) {
|
if (dt == null || !(dt instanceof FunctionDefinitionDataType)) {
|
||||||
dt = fddt;
|
dt = fddt;
|
||||||
|
|||||||
@@ -32,6 +32,12 @@ import ghidra.program.model.symbol.Namespace;
|
|||||||
*/
|
*/
|
||||||
public class DemangledDataType extends DemangledType {
|
public class DemangledDataType extends DemangledType {
|
||||||
|
|
||||||
|
protected static final CategoryPath DEMANGLER_ROOT_CATEGORY_PATH =
|
||||||
|
CategoryPath.ROOT.extend("Demangler");
|
||||||
|
|
||||||
|
protected static final CategoryPath DEMANGLER_ANONYMOUS_FUNCTION_CATEGORY_PATH =
|
||||||
|
DEMANGLER_ROOT_CATEGORY_PATH.extend("!_anon_funcs_");
|
||||||
|
|
||||||
private static final Pattern ARRAY_SUBSCRIPT_PATTERN = Pattern.compile("\\[\\d*\\]");
|
private static final Pattern ARRAY_SUBSCRIPT_PATTERN = Pattern.compile("\\[\\d*\\]");
|
||||||
|
|
||||||
public static final char SPACE = ' ';
|
public static final char SPACE = ' ';
|
||||||
@@ -145,33 +151,33 @@ public class DemangledDataType extends DemangledType {
|
|||||||
}
|
}
|
||||||
else if (isUnion()) {
|
else if (isUnion()) {
|
||||||
if (baseType == null || !(baseType instanceof Union)) {
|
if (baseType == null || !(baseType instanceof Union)) {
|
||||||
dt = new UnionDataType(getDemanglerCategoryPath(name, getNamespace()), name);
|
dt = new UnionDataType(getDemanglerCategoryPath(getNamespace()), name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (isEnum()) {
|
else if (isEnum()) {
|
||||||
if (baseType == null || !(baseType instanceof Enum)) {
|
if (baseType == null || !(baseType instanceof Enum)) {
|
||||||
|
|
||||||
if (enumType == null || INT.equals(enumType) || UNSIGNED_INT.equals(enumType)) {
|
if (enumType == null || INT.equals(enumType) || UNSIGNED_INT.equals(enumType)) {
|
||||||
// Can't tell how big an enum is, just use the size of a pointer
|
// Can't tell how big an enum is, just use the size of a pointer
|
||||||
dt = new EnumDataType(getDemanglerCategoryPath(name, getNamespace()), name,
|
dt = new EnumDataType(getDemanglerCategoryPath(getNamespace()), name,
|
||||||
dataTypeManager.getDataOrganization().getIntegerSize());
|
dataTypeManager.getDataOrganization().getIntegerSize());
|
||||||
}
|
}
|
||||||
else if (CHAR.equals(enumType) || UNSIGNED_CHAR.equals(enumType)) {
|
else if (CHAR.equals(enumType) || UNSIGNED_CHAR.equals(enumType)) {
|
||||||
dt = new EnumDataType(getDemanglerCategoryPath(name, getNamespace()), name,
|
dt = new EnumDataType(getDemanglerCategoryPath(getNamespace()), name,
|
||||||
dataTypeManager.getDataOrganization().getCharSize());
|
dataTypeManager.getDataOrganization().getCharSize());
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (SHORT.equals(enumType) || UNSIGNED_SHORT.equals(enumType)) {
|
else if (SHORT.equals(enumType) || UNSIGNED_SHORT.equals(enumType)) {
|
||||||
dt = new EnumDataType(getDemanglerCategoryPath(name, getNamespace()), name,
|
dt = new EnumDataType(getDemanglerCategoryPath(getNamespace()), name,
|
||||||
dataTypeManager.getDataOrganization().getShortSize());
|
dataTypeManager.getDataOrganization().getShortSize());
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (LONG.equals(enumType) || UNSIGNED_LONG.equals(enumType)) {
|
else if (LONG.equals(enumType) || UNSIGNED_LONG.equals(enumType)) {
|
||||||
dt = new EnumDataType(getDemanglerCategoryPath(name, getNamespace()), name,
|
dt = new EnumDataType(getDemanglerCategoryPath(getNamespace()), name,
|
||||||
dataTypeManager.getDataOrganization().getLongSize());
|
dataTypeManager.getDataOrganization().getLongSize());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
dt = new EnumDataType(getDemanglerCategoryPath(name, getNamespace()), name,
|
dt = new EnumDataType(getDemanglerCategoryPath(getNamespace()), name,
|
||||||
dataTypeManager.getDataOrganization().getIntegerSize());
|
dataTypeManager.getDataOrganization().getIntegerSize());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,7 +193,7 @@ public class DemangledDataType extends DemangledType {
|
|||||||
// I don't know what this is
|
// I don't know what this is
|
||||||
// If it isn't pointed to, or isn't a referent, then assume typedef.
|
// If it isn't pointed to, or isn't a referent, then assume typedef.
|
||||||
if (!(isReference() || isPointer())) { // Unknown type
|
if (!(isReference() || isPointer())) { // Unknown type
|
||||||
dt = new TypedefDataType(getDemanglerCategoryPath(name, getNamespace()), name,
|
dt = new TypedefDataType(getDemanglerCategoryPath(getNamespace()), name,
|
||||||
new DWordDataType());
|
new DWordDataType());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -333,7 +339,7 @@ public class DemangledDataType extends DemangledType {
|
|||||||
* Find non-builtin type
|
* Find non-builtin type
|
||||||
* @param dataTypeManager data type manager to be searched
|
* @param dataTypeManager data type manager to be searched
|
||||||
* @param dtName name of data type
|
* @param dtName name of data type
|
||||||
* @param namespace namespace associated with dtName or null if not applicable. If specified,
|
* @param namespace namespace associated with dtName or null if not applicable. If specified,
|
||||||
* a namespace-base category path will be given precedence.
|
* a namespace-base category path will be given precedence.
|
||||||
* @return data type if found, otherwise null.
|
* @return data type if found, otherwise null.
|
||||||
* @see DataTypeUtilities#findDataType(DataTypeManager, ghidra.program.model.symbol.Namespace, String, Class) for similar namespace
|
* @see DataTypeUtilities#findDataType(DataTypeManager, ghidra.program.model.symbol.Namespace, String, Class) for similar namespace
|
||||||
@@ -394,18 +400,12 @@ public class DemangledDataType extends DemangledType {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getNamespacePath(String dtName, Demangled namespace) {
|
// Recursive method
|
||||||
Demangled ns = namespace;
|
protected static CategoryPath getDemanglerCategoryPath(Demangled namespace) {
|
||||||
String namespacePath = "";
|
if (namespace == null) {
|
||||||
while (ns != null) {
|
return DEMANGLER_ROOT_CATEGORY_PATH;
|
||||||
namespacePath = "/" + ns.getName() + namespacePath;
|
|
||||||
ns = ns.getNamespace();
|
|
||||||
}
|
}
|
||||||
return namespacePath;
|
return getDemanglerCategoryPath(namespace.getNamespace()).extend(namespace.getName());
|
||||||
}
|
|
||||||
|
|
||||||
private static CategoryPath getDemanglerCategoryPath(String dtName, Demangled namespace) {
|
|
||||||
return new CategoryPath("/Demangler" + getNamespacePath(dtName, namespace));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Structure createPlaceHolderStructure(String dtName, Demangled namespace) {
|
static Structure createPlaceHolderStructure(String dtName, Demangled namespace) {
|
||||||
@@ -414,7 +414,7 @@ public class DemangledDataType extends DemangledType {
|
|||||||
}
|
}
|
||||||
StructureDataType structDT = new StructureDataType(dtName, 0);
|
StructureDataType structDT = new StructureDataType(dtName, 0);
|
||||||
structDT.setDescription("PlaceHolder Structure");
|
structDT.setDescription("PlaceHolder Structure");
|
||||||
structDT.setCategoryPath(getDemanglerCategoryPath(dtName, namespace));
|
structDT.setCategoryPath(getDemanglerCategoryPath(namespace));
|
||||||
return structDT;
|
return structDT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -692,7 +692,7 @@ public class DemangledDataType extends DemangledType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// the order of __ptr64 and __restrict can vary--with fuzzing...
|
// the order of __ptr64 and __restrict can vary--with fuzzing...
|
||||||
// but what is the natural "real symbol" order?
|
// but what is the natural "real symbol" order?
|
||||||
if (isPointer64) {
|
if (isPointer64) {
|
||||||
buffer.append(SPACE + PTR64);
|
buffer.append(SPACE + PTR64);
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ public abstract class AbstractFunctionTypeApplier extends MsTypeApplier {
|
|||||||
argsListApplier.applyTo(this);
|
argsListApplier.applyTo(this);
|
||||||
}
|
}
|
||||||
setCallingConvention(applicator, callingConvention, hasThisPointer);
|
setCallingConvention(applicator, callingConvention, hasThisPointer);
|
||||||
DataTypeNamingUtil.setMangledAnonymousFunctionName(functionDefinition, "_func");
|
DataTypeNamingUtil.setMangledAnonymousFunctionName(functionDefinition);
|
||||||
setApplied();
|
setApplied();
|
||||||
|
|
||||||
// resolvedDataType = applicator.resolveHighUse(dataType);
|
// resolvedDataType = applicator.resolveHighUse(dataType);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package ghidra.program.model.data;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import org.apache.commons.collections4.CollectionUtils;
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A category path is the full path to a particular data type
|
* A category path is the full path to a particular data type
|
||||||
@@ -52,7 +53,7 @@ public class CategoryPath implements Comparable<CategoryPath> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts an escaped String suitable for being passed in as a component of a single category
|
* Converts an escaped String suitable for being passed in as a component of a single category
|
||||||
* path string into an non-escaped string.
|
* path string into an non-escaped string.
|
||||||
* @param escapedString String that might need unescaping for characters used for delimiting
|
* @param escapedString String that might need unescaping for characters used for delimiting
|
||||||
* @return non-escaped String
|
* @return non-escaped String
|
||||||
* @see #escapeString(String)
|
* @see #escapeString(String)
|
||||||
@@ -76,7 +77,8 @@ public class CategoryPath implements Comparable<CategoryPath> {
|
|||||||
*
|
*
|
||||||
* @param parent the parent CategoryPath. Choose {@code ROOT} if needed.
|
* @param parent the parent CategoryPath. Choose {@code ROOT} if needed.
|
||||||
* @param subPathElements the array of names of sub-categories of the parent.
|
* @param subPathElements the array of names of sub-categories of the parent.
|
||||||
* @throws IllegalArgumentException if the given array is null or empty.
|
* @throws IllegalArgumentException if the parent is null, the elements list is null or empty,
|
||||||
|
* or an individual element is null
|
||||||
*/
|
*/
|
||||||
public CategoryPath(CategoryPath parent, String... subPathElements) {
|
public CategoryPath(CategoryPath parent, String... subPathElements) {
|
||||||
this(parent, Arrays.asList(subPathElements));
|
this(parent, Arrays.asList(subPathElements));
|
||||||
@@ -88,15 +90,21 @@ public class CategoryPath implements Comparable<CategoryPath> {
|
|||||||
*
|
*
|
||||||
* @param parent the parent CategoryPath. Choose {@code ROOT} if needed.
|
* @param parent the parent CategoryPath. Choose {@code ROOT} if needed.
|
||||||
* @param subPathElements the hierarchical array of sub-categories of the parent.
|
* @param subPathElements the hierarchical array of sub-categories of the parent.
|
||||||
* @throws IllegalArgumentException if the given list is null or empty.
|
* @throws IllegalArgumentException if the parent is null, the elements list is null or empty,
|
||||||
|
* or an individual element is null
|
||||||
*/
|
*/
|
||||||
public CategoryPath(CategoryPath parent, List<String> subPathElements) {
|
public CategoryPath(CategoryPath parent, List<String> subPathElements) {
|
||||||
Objects.requireNonNull(parent);
|
if (parent == null) {
|
||||||
|
throw new IllegalArgumentException("Parent category must not be null!");
|
||||||
|
}
|
||||||
if (CollectionUtils.isEmpty(subPathElements)) {
|
if (CollectionUtils.isEmpty(subPathElements)) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Category list must contain at least one string name!");
|
"Category list must contain at least one string name!");
|
||||||
}
|
}
|
||||||
name = subPathElements.get(subPathElements.size() - 1);
|
name = subPathElements.get(subPathElements.size() - 1);
|
||||||
|
if (name == null) {
|
||||||
|
throw new IllegalArgumentException("A category element must not be null!");
|
||||||
|
}
|
||||||
if (subPathElements.size() == 1) {
|
if (subPathElements.size() == 1) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
}
|
}
|
||||||
@@ -106,6 +114,36 @@ public class CategoryPath implements Comparable<CategoryPath> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a CategoryPath that extends the current path using a hierarchical array of strings
|
||||||
|
* where each string is the name of a category in the category path extension.
|
||||||
|
*
|
||||||
|
* @param subPathElements the array of names of sub-categories of the parent.
|
||||||
|
* @return the extended CategoryPath
|
||||||
|
* @throws IllegalArgumentException if an element is null
|
||||||
|
*/
|
||||||
|
public CategoryPath extend(String... subPathElements) {
|
||||||
|
if (ArrayUtils.isEmpty(subPathElements)) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new CategoryPath(this, subPathElements);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a CategoryPath that extends the current path using a hierarchical list of strings
|
||||||
|
* where each string is the name of a category in the category path extension.
|
||||||
|
*
|
||||||
|
* @param subPathElements the hierarchical array of sub-categories of the parent.
|
||||||
|
* @return the extended CategoryPath
|
||||||
|
* @throws IllegalArgumentException if an element is null
|
||||||
|
*/
|
||||||
|
public CategoryPath extend(List<String> subPathElements) {
|
||||||
|
if (CollectionUtils.isEmpty(subPathElements)) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new CategoryPath(this, subPathElements);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a category path given a forward-slash-delimited string (e.g., {@code "/aa/bb"}).
|
* Creates a category path given a forward-slash-delimited string (e.g., {@code "/aa/bb"}).
|
||||||
* If an individual path component has one or more '/' characters in it, then it can be
|
* If an individual path component has one or more '/' characters in it, then it can be
|
||||||
@@ -121,7 +159,7 @@ public class CategoryPath implements Comparable<CategoryPath> {
|
|||||||
*/
|
*/
|
||||||
// NOTE: We purposefully did not create a constructor that takes varags only, as that
|
// NOTE: We purposefully did not create a constructor that takes varags only, as that
|
||||||
// constructor, called with a single argument that would not be escaped, would conflict with
|
// constructor, called with a single argument that would not be escaped, would conflict with
|
||||||
// this constructor, which requires an escaped argument.
|
// this constructor, which requires an escaped argument.
|
||||||
public CategoryPath(String path) {
|
public CategoryPath(String path) {
|
||||||
if (path == null || path.length() == 0 || path.equals(DELIMITER_STRING)) {
|
if (path == null || path.length() == 0 || path.equals(DELIMITER_STRING)) {
|
||||||
// parent can only be null for ROOT
|
// parent can only be null for ROOT
|
||||||
|
|||||||
@@ -116,6 +116,20 @@ public class CategoryPathTest extends AbstractGTest {
|
|||||||
assertEquals("mango", c.getName());
|
assertEquals("mango", c.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testConstructorNullParent() {
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
list.add("element");
|
||||||
|
new CategoryPath(null, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testConstructorNullElement() {
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
list.add(null);
|
||||||
|
new CategoryPath(CategoryPath.ROOT, list);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConstructorParentAndList() {
|
public void testConstructorParentAndList() {
|
||||||
CategoryPath parent = new CategoryPath("/universe/earth");
|
CategoryPath parent = new CategoryPath("/universe/earth");
|
||||||
@@ -159,6 +173,49 @@ public class CategoryPathTest extends AbstractGTest {
|
|||||||
assertTrue(c.isRoot());
|
assertTrue(c.isRoot());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExtendList() {
|
||||||
|
CategoryPath parent = new CategoryPath("/universe/earth");
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
list.add("boy");
|
||||||
|
list.add("bad");
|
||||||
|
CategoryPath c = parent.extend(list);
|
||||||
|
assertEquals("/universe/earth/boy/bad", c.getPath());
|
||||||
|
assertEquals("bad", c.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExtendVarargsArray() {
|
||||||
|
CategoryPath parent = new CategoryPath("/apple/peaches");
|
||||||
|
CategoryPath c = parent.extend(new String[] { "pumpkin", "pie" });
|
||||||
|
assertEquals("pie", c.getName());
|
||||||
|
c = c.getParent();
|
||||||
|
assertEquals("pumpkin", c.getName());
|
||||||
|
c = c.getParent();
|
||||||
|
assertEquals("peaches", c.getName());
|
||||||
|
c = c.getParent();
|
||||||
|
assertEquals("apple", c.getName());
|
||||||
|
c = c.getParent();
|
||||||
|
assertEquals("", c.getName());
|
||||||
|
assertTrue(c.isRoot());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExtendVarargs() {
|
||||||
|
CategoryPath parent = new CategoryPath("/apple/peaches");
|
||||||
|
CategoryPath c = parent.extend("pumpkin", "pie");
|
||||||
|
assertEquals("pie", c.getName());
|
||||||
|
c = c.getParent();
|
||||||
|
assertEquals("pumpkin", c.getName());
|
||||||
|
c = c.getParent();
|
||||||
|
assertEquals("peaches", c.getName());
|
||||||
|
c = c.getParent();
|
||||||
|
assertEquals("apple", c.getName());
|
||||||
|
c = c.getParent();
|
||||||
|
assertEquals("", c.getName());
|
||||||
|
assertTrue(c.isRoot());
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testConstructorBadCtorParam_empty_path_element() {
|
public void testConstructorBadCtorParam_empty_path_element() {
|
||||||
new CategoryPath("//");
|
new CategoryPath("//");
|
||||||
|
|||||||
Reference in New Issue
Block a user