blob: 79eaf963bd648100cf149843f44eb80554117ab9 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2015 Ericsson and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Ericsson - initial API and implementation
* Alvaro Sanchez-Leon (Ericsson) - Make Registers View specific to a frame (Bug 323552)
* Alvaro Sanchez-Leon (Ericsson) - Allow user to edit the register groups (Bug 235747)
* Simon Marchi (Ericsson) - Adapt test code to thread platform compatibility layer.
*******************************************************************************/
package org.eclipse.cdt.tests.dsf.gdb.tests;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
import org.eclipse.cdt.dsf.concurrent.ImmediateCountingRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Query;
import org.eclipse.cdt.dsf.datamodel.CompositeDMContext;
import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.debug.service.IFormattedValues;
import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext;
import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData;
import org.eclipse.cdt.dsf.debug.service.IRegisters;
import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext;
import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMData;
import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterGroupDMContext;
import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterGroupDMData;
import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegistersChangedDMEvent;
import org.eclipse.cdt.dsf.debug.service.IRegisters2;
import org.eclipse.cdt.dsf.debug.service.IRunControl;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext;
import org.eclipse.cdt.dsf.debug.service.IRunControl.StepType;
import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
import org.eclipse.cdt.dsf.gdb.service.GDBRegisters;
import org.eclipse.cdt.dsf.mi.service.IMICommandControl;
import org.eclipse.cdt.dsf.mi.service.MIRegisters.MIRegisterDMC;
import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent;
import org.eclipse.cdt.dsf.mi.service.command.output.MIDataListRegisterNamesInfo;
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.tests.dsf.gdb.framework.BaseParametrizedTestCase;
import org.eclipse.cdt.tests.dsf.gdb.framework.ServiceEventWaitor;
import org.eclipse.cdt.tests.dsf.gdb.framework.SyncUtil;
import org.eclipse.cdt.tests.dsf.gdb.launching.TestsPlugin;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class MIRegistersTest extends BaseParametrizedTestCase {
// Static list of register names as obtained directly from GDB.
// We make it static it does not get re-set for every test
protected static List<String> fRegisterNames = null;
@BeforeClass
public static void initializeGlobals() {
// In case we run multiple GDB versions of this test
// in the same suite, we need to re-initialize the registers
// as they may change between GDB versions.
fRegisterNames = null;
}
protected List<String> get_X86_REGS() throws Throwable {
if (fRegisterNames == null) {
// The tests must run on different machines, so the set of registers can change.
// To deal with this we ask GDB for the list of registers.
// Note that we send an MI Command in this code and do not use the IRegister service;
// this is because we want to test the service later, comparing it to what we find
// by asking GDB directly.
Query<MIDataListRegisterNamesInfo> query = new Query<MIDataListRegisterNamesInfo>() {
@Override
protected void execute(DataRequestMonitor<MIDataListRegisterNamesInfo> rm) {
IMICommandControl controlService = fServicesTracker.getService(IMICommandControl.class);
IContainerDMContext containerDmc = DMContexts.getAncestorOfType(fCompositeDmc, IContainerDMContext.class);
controlService.queueCommand(controlService.getCommandFactory().createMIDataListRegisterNames(containerDmc), rm);
}
};
fSession.getExecutor().execute(query);
MIDataListRegisterNamesInfo data = query.get();
String[] names = data.getRegisterNames();
// Remove registers with empty names since the service also
// remove them. I don't know why GDB returns such empty names.
fRegisterNames = new LinkedList<String>();
for (String name : names) {
if (!name.isEmpty()) {
fRegisterNames.add(name);
}
}
}
return fRegisterNames;
}
/*
* Name of the executable
*/
private static final String EXEC_NAME = "MultiThread.exe";
private static final String SOURCE_NAME = "MultiThread.cc";
private static final String GROUP_X = "GroupX";
private static final String GROUP_Y = "GroupY";
private static final String PROPOSE_GROUP_NAME_BASE = "Group_";
private DsfSession fSession;
private DsfServicesTracker fServicesTracker;
private IDMContext fCompositeDmc;
private IRegisters2 fRegService;
private IRunControl fRunControl;
private Integer fGroupNameSuffix;
@Override
public void doBeforeTest() throws Exception {
super.doBeforeTest();
fSession = getGDBLaunch().getSession();
resolveLineTagLocations(SOURCE_NAME, MIRunControlTest.LINE_TAGS);
Runnable runnable = new Runnable() {
@Override
public void run() {
// We obtain the services we need after the new
// launch has been performed
fServicesTracker = new DsfServicesTracker(TestsPlugin.getBundleContext(), fSession.getId());
fRegService = (IRegisters2) fServicesTracker.getService(IRegisters.class);
fRunControl = fServicesTracker.getService(IRunControl.class);
}
};
fSession.getExecutor().submit(runnable).get();
//resolve the execution context
IFrameDMContext frameDmc = SyncUtil.getStackFrame(getInitialStoppedEvent().getDMContext(), 0);
//resolve the container context
IContainerDMContext containerDmc = DMContexts.getAncestorOfType(getInitialStoppedEvent().getDMContext(), IContainerDMContext.class);
//The container dmc is expected to contain the frame and container context
fCompositeDmc = new CompositeDMContext(new IDMContext[] { containerDmc, frameDmc });
fGroupNameSuffix = 0;
}
@Override
protected void setLaunchAttributes() {
super.setLaunchAttributes();
setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, EXEC_PATH + EXEC_NAME);
}
@Override
public void doAfterTest() throws Exception {
super.doAfterTest();
if (fServicesTracker!=null) fServicesTracker.dispose();
fRegService = null;
}
/*
* This is a common support method which gets the Register Group Information from target
*/
private IRegisterGroupDMContext getTargetRegisterGroup() throws Throwable {
//Get all the registers from the Container (Process)
IRegisterDMContext[] registers = getRegisters(fCompositeDmc);
assertTrue(registers.length > 0);
//Get the register group from any of the register contexts
IRegisterGroupDMContext regGroupsDMC = DMContexts.getAncestorOfType(registers[0], IRegisterGroupDMContext.class);
assertNotNull(regGroupsDMC);
return regGroupsDMC;
}
/*
* This is a common support method which gets the Registers names.
*/
private IRegisterDMContext[] getAllRegisters(final IFrameDMContext frameDmc) throws Throwable {
Query<IRegisterDMContext[]> queryRegisters = new Query<IRegisterDMContext[]>() {
@Override
protected void execute(DataRequestMonitor<IRegisterDMContext[]> rm) {
fRegService.getRegisters(new CompositeDMContext(new IDMContext[] { fCompositeDmc, frameDmc }), rm);
}
};
fSession.getExecutor().execute(queryRegisters);
IRegisterDMContext[] regContexts = queryRegisters.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS);
assertEquals("Wrong number of registers", get_X86_REGS().size(), regContexts.length);
return regContexts;
}
/*
* Get the Registers for the specified composite context
*/
private IRegisterDMContext[] getRegisters(final IDMContext dmc) throws Throwable {
Query<IRegisterDMContext[]> queryRegistersDmc = new Query<IRegisterDMContext[]>() {
@Override
protected void execute(DataRequestMonitor<IRegisterDMContext[]> rm) {
fRegService.getRegisters(dmc, rm);
}
};
fRegService.getExecutor().submit(queryRegistersDmc);
IRegisterDMContext[] regContexts = queryRegistersDmc.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS);
return(regContexts);
}
/*
* This is a common support method which gets the Register context of root group over the specified frame context
*/
private IRegisterDMContext[] getTargetRegisters(final IFrameDMContext frameDmc) throws Throwable {
IRegisterDMContext[] regContexts = getRegisters(new CompositeDMContext(new IDMContext[] { fCompositeDmc, frameDmc}));
assertEquals("Wrong number of registers", get_X86_REGS().size(), regContexts.length);
return regContexts;
}
/*************************************************************************
*
* The tests for the register service.
*
*************************************************************************/
@Test
public void resolveTargetRegisterGroup() throws Throwable {
final IRegisterGroupDMContext regGroupsDMC = getTargetRegisterGroup();
IRegisterGroupDMData data = getRegisterGroupData(regGroupsDMC);
assertEquals("Main register group's name", "General Registers", data.getName());
}
@Test
public void resolveTargetRegistersLength() throws Throwable {
MIStoppedEvent stoppedEvent = getInitialStoppedEvent();
IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0);
final IRegisterDMContext[] regDMCs = getAllRegisters(frameDmc);
assertEquals("Wrong number of registers", get_X86_REGS().size(), regDMCs.length);
}
@Test
public void getRegisters() throws Throwable {
MIStoppedEvent stoppedEvent = getInitialStoppedEvent();
IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0);
final IRegisterDMContext[] regDMCs = getAllRegisters(frameDmc);
List<String> regNames = get_X86_REGS();
IRegisterDMData[] datas = getRegistersData(regDMCs);
for(IRegisterDMData data: datas){
String regName = data.getName();
assertTrue("GDB does not support register name: " + regName, regNames.contains(regName));
}
}
private IRegisterDMData[] getRegistersData(final IRegisterDMContext[] regDMCs) throws InterruptedException, ExecutionException {
Query<IRegisterDMData[]> query = new Query<IRegisterDMData[]>() {
@Override
protected void execute(DataRequestMonitor<IRegisterDMData[]> rm) {
final IRegisterDMData[] datas = new IRegisterDMData[regDMCs.length];
rm.setData(datas);
final CountingRequestMonitor countingRm = new ImmediateCountingRequestMonitor(rm);
countingRm.setDoneCount(regDMCs.length);
for (int i = 0; i < regDMCs.length; i++) {
final int index = i;
fRegService.getRegisterData(regDMCs[index], new ImmediateDataRequestMonitor<IRegisterDMData>(countingRm) {
@Override
protected void handleSuccess() {
datas[index] = getData();
countingRm.done();
}
});
}
}
};
fSession.getExecutor().execute(query);
return query.get();
}
private String getModelDataForRegisterDataValue(IFrameDMContext frameDmc, String format, int regNo) throws Throwable {
final IRegisterDMContext[] regDMCs = getAllRegisters(frameDmc);
return getModelDataForRegisterDataValue(regDMCs[regNo], format);
}
private String getModelDataForRegisterDataValue(IRegisterDMContext registerDmc, String format) throws Throwable {
final FormattedValueDMContext valueDmc = fRegService.getFormattedValueContext(registerDmc, format);
Query<FormattedValueDMData> queryFormattedData = new Query<FormattedValueDMData>() {
@Override
protected void execute(DataRequestMonitor<FormattedValueDMData> rm) {
fRegService.getFormattedExpressionValue(valueDmc, rm);
}
};
fRegService.getExecutor().submit(queryFormattedData);
FormattedValueDMData data = queryFormattedData.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS);
return data.getFormattedValue();
}
@Test
public void getModelDataForRegisterDataValueInDifferentNumberFormats() throws Throwable {
MIStoppedEvent stoppedEvent = getInitialStoppedEvent();
IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0);
// Make sure register #0 contains a known value.
writeRegister(frameDmc, 0, "0x1234", IFormattedValues.HEX_FORMAT);
String val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.NATURAL_FORMAT, 0);
assertThat(Long.parseLong(val), equalTo(0x1234L));
val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.HEX_FORMAT, 0);
assertTrue("Register Value is not in HEX_FORMAT: " + val, val.startsWith("0x"));
assertThat(Long.parseLong(val.substring(2), 16), equalTo(0x1234L));
val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.BINARY_FORMAT, 0);
assertThat(Long.parseLong(val, 2), equalTo(0x1234L));
val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.DECIMAL_FORMAT, 0);
assertThat(Long.parseLong(val), equalTo(0x1234L));
val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.OCTAL_FORMAT, 0);
assertTrue("Register Value is not in OCTAL_FORMAT: " + val, val.startsWith("0"));
assertThat(Long.parseLong(val.substring(1), 8), equalTo(0x1234L));
}
@Test
public void compareRegisterForMultipleExecutionContexts() throws Throwable {
MIStoppedEvent stoppedEvent = SyncUtil.runToLocation(SOURCE_NAME + ':'
+ getLineForTag("LINE_MAIN_ALL_THREADS_STARTED"));
// Get the thread IDs
final IContainerDMContext containerDmc = DMContexts.getAncestorOfType(stoppedEvent.getDMContext(), IContainerDMContext.class);
Query<IExecutionDMContext[]> queryExecutionContexts = new Query<IExecutionDMContext[]>() {
@Override
protected void execute(DataRequestMonitor<IExecutionDMContext[]> rm) {
fRunControl.getExecutionContexts(containerDmc, rm);
}
};
fRegService.getExecutor().submit(queryExecutionContexts);
IExecutionDMContext[] ctxts = queryExecutionContexts.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS);
assertNotNull(ctxts);
assertTrue(ctxts.length > 1);
// Get stack frame for thread 2
IFrameDMContext frameDmc2 = SyncUtil.getStackFrame(ctxts[1], 0);
String thread2RegVal0 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 0);
String thread2RegVal1 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 1);
String thread2RegVal2 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 2);
String thread2RegVal3 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 3);
String thread2RegVal4 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 4);
String thread2RegVal5 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 5);
// Get stack frame for thread 1
IFrameDMContext frameDmc1 = SyncUtil.getStackFrame(ctxts[0], 0);
getModelDataForRegisterDataValue(frameDmc1, IFormattedValues.NATURAL_FORMAT, 0);
// Re-set the execution context to 2 and Fetch from the Cache
String dupliThread2RegVal0 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 0);
String dupliThread2RegVal1 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 1);
String dupliThread2RegVal2 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 2);
String dupliThread2RegVal3 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 3);
String dupliThread2RegVal4 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 4);
String dupliThread2RegVal5 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 5);
// If Values not equal , then context haven't been re-set properly
assertEquals("Multiple context not working. Execution Context is not reset to 2", thread2RegVal0, dupliThread2RegVal0);
assertEquals("Multiple context not working. Execution Context is not reset to 2", thread2RegVal1, dupliThread2RegVal1);
assertEquals("Multiple context not working. Execution Context is not reset to 2", thread2RegVal2, dupliThread2RegVal2);
assertEquals("Multiple context not working. Execution Context is not reset to 2", thread2RegVal3, dupliThread2RegVal3);
assertEquals("Multiple context not working. Execution Context is not reset to 2", thread2RegVal4, dupliThread2RegVal4);
assertEquals("Multiple context not working. Execution Context is not reset to 2", thread2RegVal5, dupliThread2RegVal5);
}
private void writeRegister(IFrameDMContext frameDmc, final int regIndex, final String regValue, final String formatId) throws Throwable {
final IRegisterDMContext[] regDMCs = getAllRegisters(frameDmc);
writeRegister(regDMCs[regIndex], regValue, formatId);
}
private void writeRegister(final IRegisterDMContext registerDmc, final String regValue, final String formatId) throws Throwable {
Query<Object> queryAction = new Query<Object>() {
@Override
protected void execute(DataRequestMonitor<Object> rm) {
fRegService.writeRegister(registerDmc, regValue, formatId, rm);
}
};
fRegService.getExecutor().submit(queryAction);
queryAction.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS);
}
/**
* Waits for IRegistersChangedDMEvent(s) during a time interval, collects them and returns them after timeout
*/
private List<IRegistersChangedDMEvent> writeRegisterWaitNotication(final IRegisterDMContext registerDmc, final String regValue, final String formatId)
throws Throwable {
ServiceEventWaitor<IRegistersChangedDMEvent> eventWaitor =
new ServiceEventWaitor<IRegistersChangedDMEvent>(fSession, IRegistersChangedDMEvent.class);
writeRegister(registerDmc, regValue, formatId);
return eventWaitor.waitForEvents(TestsPlugin.massageTimeout(3000));
}
@Test
public void writeRegisterNaturalFormat() throws Throwable {
MIStoppedEvent stoppedEvent = getInitialStoppedEvent();
IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0);
String regValue = "10";
int regIndex = 3;
writeRegister(frameDmc, 3, regValue, IFormattedValues.NATURAL_FORMAT);
String val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.NATURAL_FORMAT, regIndex);
assertEquals("Failed writing register", regValue, val);
}
@Test
public void writeRegisterHEXFormat() throws Throwable {
MIStoppedEvent stoppedEvent = getInitialStoppedEvent();
IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0);
String regValue = "0x10";
int regIndex = 3;
writeRegister(frameDmc, 3, regValue, IFormattedValues.HEX_FORMAT);
String val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.HEX_FORMAT, regIndex);
assertEquals("Failed writing register", regValue, val);
}
@Test
public void writeRegisterBinaryFormat() throws Throwable {
MIStoppedEvent stoppedEvent = getInitialStoppedEvent();
IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0);
String regValue = "100101001";
int regIndex = 3;
writeRegister(frameDmc, 3, regValue, IFormattedValues.BINARY_FORMAT);
String val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.BINARY_FORMAT, regIndex);
assertEquals("Failed writing register", regValue, val);
}
@Test
public void writeRegisterOctalFormat() throws Throwable {
MIStoppedEvent stoppedEvent = getInitialStoppedEvent();
IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0);
// String regValue = "10";
String regValue = "012";
int regIndex = 3;
writeRegister(frameDmc, 3, regValue, IFormattedValues.OCTAL_FORMAT);
String val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.OCTAL_FORMAT, regIndex);
assertEquals("Failed writing register", regValue, val);
}
/**
* This test validates retrieval of different values for the same register used on different frames
*/
@Test
public void frameSpecificValues() throws Throwable {
// Step to a multi-level stack to be able to test different stack frames
SyncUtil.runToLocation("PrintHello");
MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_OVER);
int depth = SyncUtil.getStackDepth(stoppedEvent.getDMContext());
// we need at least 2 levels of stack frame to continue this test.
// note: the depth is glibc-version-dependent
assertTrue(depth >= 2);
// Resolve the register name of the stack pointer
String sp_name = resolveStackPointerName();
assertNotNull(sp_name);
// Get the stack pointer value for frame0
IFrameDMContext frame0 = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0);
IRegisterDMContext[] registers_f0 = getTargetRegisters(frame0);
MIRegisterDMC sp_reg_f0 = (MIRegisterDMC) findStackPointerRegister(sp_name, registers_f0);
assertNotNull(sp_reg_f0);
String sp_f0_str = getModelDataForRegisterDataValue(frame0, IFormattedValues.HEX_FORMAT, sp_reg_f0.getRegNo());
// Get the stack pointer value for frame1
IFrameDMContext frame1 = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 1);
IRegisterDMContext[] registers_f1 = getTargetRegisters(frame1);
MIRegisterDMC sp_reg_f1 = (MIRegisterDMC) findStackPointerRegister(sp_name, registers_f1);
assertNotNull(sp_reg_f1);
String sp_f1_str = getModelDataForRegisterDataValue(frame1, IFormattedValues.HEX_FORMAT, sp_reg_f1.getRegNo());
//The stack pointer's are not expected to be the same among frames
assertFalse("Stack pointers shall be different among frames", sp_f0_str.equals(sp_f1_str));
}
private IRegisterDMContext findStackPointerRegister(String sp_name, IRegisterDMContext[] registerDMCs) throws InterruptedException, ExecutionException {
IRegisterDMData[] registersData = getRegistersData(registerDMCs);
for (int i = 0; i < registersData.length; i++) {
IRegisterDMData registerData = registersData[i];
if (registerData.getName().equals(sp_name)) {
return registerDMCs[i];
}
}
return null;
}
private String resolveStackPointerName() throws Throwable {
List<String> regNames = get_X86_REGS();
// for 64 bits
String sp_name = "rsp";
if (regNames.contains(sp_name)) {
return sp_name;
}
// for 32 bits
sp_name = "esp";
if (regNames.contains(sp_name)) {
return sp_name;
}
// for 16 bits
sp_name = "sp";
if (regNames.contains(sp_name)) {
return sp_name;
}
return null;
}
@Test
public void getRegisterGroupsData() throws Throwable {
int grpsIncrement = 3;
//Group name to Group description
Map<String, String> groupNameToDescMap = new HashMap<>();
groupNameToDescMap.put("Group_1", "");
groupNameToDescMap.put("Group_2", "");
groupNameToDescMap.put("Group_3", "");
groupNameToDescMap.put("General Registers", "General Purpose and FPU Register Group");
//Tracking groups found
Set<String> groupsFound = new HashSet<>();
addRegisterGroups(grpsIncrement);
final IRegisterDMContext[] regInGroup = getRegisters(0, 4);
IRegisterGroupDMData[] groupsData = getRegisterGroupsData(regInGroup[0]);
//increment + root
assertEquals(grpsIncrement + 1, groupsData.length);
for (IRegisterGroupDMData grpData: groupsData) {
// Validate group name
assertTrue(groupNameToDescMap.containsKey(grpData.getName()));
String grpDataDesc = grpData.getDescription();
String expectedName = groupNameToDescMap.get(grpData.getName());
//Validate group description
assertEquals(expectedName, grpDataDesc);
groupsFound.add(grpData.getName());
}
//Make sure all expected groups were found
assertEquals(groupNameToDescMap.size(), groupsFound.size());
}
@Test
public void canAddRegisterGroup() throws Throwable {
// only root group expected
final IRegisterGroupDMContext[] groups = getRegisterGroups(1);
assertEquals("Unexpected groups present, only root was expected", 1, groups.length);
assertTrue("Can not Add register groups", canAddRegisterGroup(groups[0]));
}
@Test
public void canNotEditRootRegisterGroup() throws Throwable {
// only root group expected
final IRegisterGroupDMContext[] groups = getRegisterGroups(1);
assertEquals("Unexpected groups present, only root was expected", 1, groups.length);
assertFalse("Not expected to allow the editing of the root register group", canEditRegisterGroup(groups[0]));
}
@Test
public void canNotEditUnknownRegisterGroup() throws Throwable {
// only root group expected
final IRegisterGroupDMContext group = new IRegisterGroupDMContext() {
@Override
public String getSessionId() {
return "session";
}
@Override
public IDMContext[] getParents() {
return new IDMContext[0];
}
@Override
public <T> T getAdapter(Class<T> adapter) {
return null;
}
};
assertFalse("Not expected to allow editing of a non registered group", canEditRegisterGroup(group));
}
@Test
public void canEditRegisterGroup() throws Throwable {
// only root group expected
final IRegisterGroupDMContext[] groups = getRegisterGroups(1);
assertEquals("Unexpected groups present, only root was expected", 1, groups.length);
IRegisterGroupDMContext group = addDefaultUserGroup();
assertTrue("Was not allowed to edit register group", canEditRegisterGroup(group));
}
/**
* @throws Throwable
*/
@Test
public void addRegisterGroups() throws Throwable {
// Define a subset of registers to create a register group (from, to)
final IRegisterDMContext[] regInGroup = getRegisters(0, 4);
// Adding three groups with default names
String groupName = proposeGroupName();
addGroup(groupName, regInGroup);
// Resolve the new group's sequence number from the pattern "Group_sequenceN"
int starting_sequence = resolveGroupNameSequence(groupName);
groupName = proposeGroupName();
addGroup(groupName, regInGroup);
groupName = proposeGroupName();
addGroup(groupName, regInGroup);
// Retrieve the existing groups, expected and validated to root + 3
IRegisterGroupDMContext[] groups = getRegisterGroups(4);
// The groups are returned in reversed order to present latest created
// first i.e. index 0
// The newest group shall be at the top then i.e. 0
//Add a valid execution context to resolve the register values
IFrameDMContext frameDmc = DMContexts.getAncestorOfType(regInGroup[0], IFrameDMContext.class);
CompositeDMContext compositeDmc = new CompositeDMContext(new IDMContext[]{frameDmc, groups[0]});
IRegisterDMContext[] readRegisters = getRegisters(compositeDmc);
// Same order, same data and same values are expected, although different instances to different parents
assertTrue(sameData(regInGroup, readRegisters, false));
// Assert the last created group name
IRegisterGroupDMData groupData = getRegisterGroupData(groups[0]);
// 2 additional groups after creation of the base group in this test case
assertEquals("Group_" + (starting_sequence + 2), groupData.getName());
}
@Test
public void editRegisterGroup() throws Throwable {
// Get Register Groups
IRegisterGroupDMContext[] groups = getRegisterGroups(1);
assertEquals("unexpected groups present", 1, groups.length);
IRegisterGroupDMContext group = addDefaultUserGroup();
IRegisterDMContext[] origRegisters = getRegisters(group);
// Assert the default group name
IRegisterGroupDMData groupData = getRegisterGroupData(group);
assertEquals(GROUP_X, groupData.getName());
// Registers to associate to exiting default group
IRegisterDMContext[] newRegisters = getRegisters(5, 9);
// A different set of registers being assigned to the group
assertFalse((sameData(origRegisters, newRegisters, false)));
// Modify the name and associated registers of the default group
editGroup(group, GROUP_Y, newRegisters);
groupData = getRegisterGroupData(group);
assertEquals(GROUP_Y, groupData.getName());
IFrameDMContext frameDmc = DMContexts.getAncestorOfType(newRegisters[0], IFrameDMContext.class);
//Read the context with a valid execution context
CompositeDMContext compositeDmc = new CompositeDMContext(new IDMContext[]{group, frameDmc});
IRegisterDMContext[] readRegisters = getRegisters(compositeDmc);
//Same data but not from the same parent group, newRegisters from root, readRegisters from GroupY
assertTrue(sameData(newRegisters, readRegisters, false));
}
@Test
public void canRemoveRegisterGroup() throws Throwable {
// only root group expected
IRegisterGroupDMContext[] groups = getRegisterGroups(1);
assertFalse("Removal of root register group shall not be allowed", canRemoveRegisterGroups(groups));
//Add another two groups
// Define a subset of registers to create a register group (from, to)
final IRegisterDMContext[] regInGroup = getRegisters(0, 4);
// Adding three groups with default names
String groupName = proposeGroupName();
addGroup(groupName, regInGroup);
groupName = proposeGroupName();
addGroup(groupName, regInGroup);
groupName = proposeGroupName();
addGroup(groupName, regInGroup);
// Retrieve the existing groups, expected and validated to root + 3
groups = getRegisterGroups(4);
//Remove the root group from the list, so the remaining can be validated
//as can be removed
groups = Arrays.copyOfRange(groups, 0, groups.length-1);
assertTrue("Not allowing removal of groups", canRemoveRegisterGroups(groups));
//remove the non root groups and validate the result
removeGroups(groups);
groups = getRegisterGroups(1);
}
@Test
public void removeRegisterGroups() throws Throwable {
int grpsIncrement = 3;
addRegisterGroups(grpsIncrement);
// Retrieve the existing groups, expected and validated to 1 root + 3
IRegisterGroupDMContext[] groups = getRegisterGroups(1 + grpsIncrement);
// remove one and assert the new size
IRegisterGroupDMContext[] remGroups = new IRegisterGroupDMContext[] { groups[0] };
removeGroups(remGroups);
getRegisterGroups(grpsIncrement); // assert this to root + 2
// remove two more and assert the new size
remGroups = new IRegisterGroupDMContext[] { groups[1], groups[2] };
removeGroups(remGroups);
getRegisterGroups(1); // assert this to only one i.e. root
// attempt to remove root -- Shall not be allowed
remGroups = new IRegisterGroupDMContext[] { groups[3] };
removeGroups(remGroups);
getRegisterGroups(1); // assert this to only one i.e. root
}
/**
* The root group shall not be deleted i.e. ignore and preserved
*/
@Test
public void removeRegisterGroupsWithRoot() throws Throwable {
int grpsIncrement = 3;
addRegisterGroups(grpsIncrement);
// Retrieve the existing groups, expected and validated to 1 root + 3
IRegisterGroupDMContext[] groups = getRegisterGroups(1 + grpsIncrement);
// Attempt to remove all i.e. root + user defined register groups
removeGroups(groups);
getRegisterGroups(1); // assert root is preserved
}
@Test
public void canRestoreRegisterGroups() throws Throwable {
int grpsIncrement = 3;
addRegisterGroups(grpsIncrement);
//Always able to restore to default
assertTrue(canRestoreDefaultGroups());
}
@Test
public void restoreRegisterGroups() throws Throwable {
int grpsIncrement = 3;
addRegisterGroups(grpsIncrement);
restoreDefaultGroups();
// assert all groups are gone except root
getRegisterGroups(1);
}
@Test
public void getRegisterNames() throws Throwable {
int grpsIncrement = 2;
addRegisterGroups(grpsIncrement);
IRegisterGroupDMContext[] groups = getRegisterGroups(3); // root +
// increment
IRegisterDMContext[] regDmcs1 = getRegisters(groups[0]);
IRegisterDMContext[] regDmcs2 = getRegisters(groups[1]);
IRegisterDMData[] regNames1 = getRegistersData(regDmcs1);
IRegisterDMData[] regNames2 = getRegistersData(regDmcs2);
// assert the register names match on both groups
assertTrue(sameRegisterNames(regNames1, regNames2));
}
@Test
public void saveAndReadRegisterGroupData() throws Throwable {
//Only the default group is expected
int starting_sequence = 0;
int grpsIncrement = 2;
addRegisterGroups(grpsIncrement);
getRegisterGroups(3); // root + 2
//The two steps below would ideally use a shutdown and the start of a new launch configuration within the same case.
//However the approach below accomplishes verification of saving and reading with out over complicating the current base test case structure.
//trigger groups saving
saveRegGroups();
//trigger group reading from launch configuration
resetRegService();
IRegisterGroupDMContext[] groups = getRegisterGroups(3); // root + 2
// Assert the last created group name
IRegisterGroupDMData groupData = getRegisterGroupData(groups[0]);
// 2 additional groups after creation of the base group in this test case
assertEquals("Group_" + (starting_sequence + grpsIncrement), groupData.getName());
}
/**
* All groups shall be able to write / update register values, These new value shall be propagated to any other
* group(s) containing this register
*/
@Test
public void writeRegisterFromUserGroup() throws Throwable {
// Define a subset of registers common to other register groups
// indexes (from, to)
final IRegisterDMContext[] regInRootGroup = getRegisters(0, 4);
String oirigVal = getModelDataForRegisterDataValue(regInRootGroup[0], IFormattedValues.NATURAL_FORMAT);
// Get a handle to the execution contexts, same frame execution context for all actions
IFrameDMContext frameDmc = DMContexts.getAncestorOfType(regInRootGroup[0], IFrameDMContext.class);
// create two user groups containing the same registers (new register instances based on root)
String groupNameOne = proposeGroupName();
addGroup(groupNameOne, regInRootGroup);
String groupNameTwo = proposeGroupName();
addGroup(groupNameTwo, regInRootGroup);
// Retrieve the existing groups, expected and validated to root + 2
IRegisterGroupDMContext[] groups = getRegisterGroups(3);
// Read registers from group one
// index 0 -> root group; index1 -> group one, index2 -> group Two
CompositeDMContext compositeDmc = new CompositeDMContext(new IDMContext[] { frameDmc, groups[1] });
IRegisterDMContext[] groupOneRegisters = getRegisters(compositeDmc);
// write a register value from register group one, register index 0
Long iWrittenVal = Long.valueOf(oirigVal) + Long.valueOf(5);
String writtenValue = iWrittenVal.toString();
List<IRegistersChangedDMEvent> eventNotifications = writeRegisterWaitNotication(groupOneRegisters[0],
writtenValue, IFormattedValues.NATURAL_FORMAT);
// Validate IRegistersChangedDMEvent event notifications, one notification per group to trigger UI refresh
assertNotNull("No IRegistersChangedDMEvent were generated from the register value update", eventNotifications);
assertEquals("Incorrect number of IRegistersChangedDMEvent notifications, expecting one per group", 3,
eventNotifications.size());
// read the register value from user group two
compositeDmc = new CompositeDMContext(new IDMContext[] { frameDmc, groups[2] });
IRegisterDMContext[] groupTwoRegisters = getRegisters(compositeDmc);
String readVal = getModelDataForRegisterDataValue(groupTwoRegisters[0], IFormattedValues.NATURAL_FORMAT);
// assert the value from group two has been updated
assertEquals(
"Register[0] Value read from group two does not correspond to updated value written from group one",
writtenValue, readVal);
// read the register value from the root register group
readVal = getModelDataForRegisterDataValue(regInRootGroup[0], IFormattedValues.NATURAL_FORMAT);
// assert the value from root group has also been updated
assertEquals(
"Register[0] Value read from root group does not correspond to updated value written from group one",
writtenValue, readVal);
}
/**
* Get an array with all available register groups
*/
private IRegisterGroupDMContext[] getRegisterGroups() throws Throwable {
Query<IRegisterGroupDMContext[]> queryGroupsCtx = new Query<IRegisterGroupDMContext[]>() {
@Override
protected void execute(DataRequestMonitor<IRegisterGroupDMContext[]> rm) {
fRegService.getRegisterGroups(fCompositeDmc, rm);
}
};
fRegService.getExecutor().execute(queryGroupsCtx);
IRegisterGroupDMContext[] regGroupsDMCs = queryGroupsCtx.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS);
return (regGroupsDMCs);
}
/**
* Request the existing groups and validate an expected count
*/
private IRegisterGroupDMContext[] getRegisterGroups(int expectedCount) throws Throwable {
IRegisterGroupDMContext[] regGroupsDMCs = getRegisterGroups();
assertEquals("Number of groups present", //$NON-NLS-1$
expectedCount, regGroupsDMCs.length);
return regGroupsDMCs;
}
private String proposeGroupName() throws Throwable {
return PROPOSE_GROUP_NAME_BASE + ++fGroupNameSuffix;
}
private boolean canAddRegisterGroup(final IDMContext context) throws Throwable {
Query<Boolean> queryAction = new Query<Boolean>() {
@Override
protected void execute(DataRequestMonitor<Boolean> rm) {
fRegService.canAddRegisterGroup(context, rm);
}
};
fRegService.getExecutor().submit(queryAction);
return queryAction.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS);
}
private boolean canEditRegisterGroup(final IRegisterGroupDMContext context) throws Throwable {
Query<Boolean> queryAction = new Query<Boolean>() {
@Override
protected void execute(DataRequestMonitor<Boolean> rm) {
fRegService.canEditRegisterGroup(context, rm);
}
};
fRegService.getExecutor().submit(queryAction);
return queryAction.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS);
}
private boolean canRemoveRegisterGroups(final IRegisterGroupDMContext[] groupsContext) throws Throwable {
Query<Boolean> queryAction = new Query<Boolean>() {
@Override
protected void execute(DataRequestMonitor<Boolean> rm) {
fRegService.canRemoveRegisterGroups(groupsContext, rm);
}
};
fRegService.getExecutor().submit(queryAction);
return queryAction.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS);
}
private void addGroup(final String groupName, final IRegisterDMContext[] regIndexes) throws Throwable {
if (regIndexes == null || regIndexes.length < 1) {
fail("Invalid argument regIndexes");
return;
}
Query<Object> queryAction = new Query<Object>() {
@Override
protected void execute(DataRequestMonitor<Object> rm) {
IContainerDMContext contDmc = DMContexts.getAncestorOfType(regIndexes[0], IContainerDMContext.class);
fRegService.addRegisterGroup(contDmc, groupName, regIndexes, rm);
}
};
fRegService.getExecutor().submit(queryAction);
queryAction.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS);
}
private void editGroup(final IRegisterGroupDMContext group, final String newGroupName, final IRegisterDMContext[] regIndexes) throws Throwable {
Query<Object> queryAction = new Query<Object>() {
@Override
protected void execute(DataRequestMonitor<Object> rm) {
fRegService.editRegisterGroup(group, newGroupName, regIndexes, rm);
}
};
fRegService.getExecutor().submit(queryAction);
queryAction.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS);
}
private void removeGroups(final IRegisterGroupDMContext[] groups) throws Throwable {
Query<Object> queryAction = new Query<Object>() {
@Override
protected void execute(DataRequestMonitor<Object> rm) {
fRegService.removeRegisterGroups(groups, rm);
}
};
fRegService.getExecutor().submit(queryAction);
queryAction.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS);
}
private boolean canRestoreDefaultGroups() throws Throwable {
Query<Boolean> queryCanRestore = new Query<Boolean>() {
@Override
protected void execute(DataRequestMonitor<Boolean> rm) {
//selection context not used for the time being
fRegService.canRestoreDefaultGroups(null, rm);
}
};
fRegService.getExecutor().submit(queryCanRestore);
//Validate, we can always restore to defaults
return queryCanRestore.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS);
}
private void restoreDefaultGroups() throws Throwable {
Query<Object> queryRestore = new Query<Object>() {
@Override
protected void execute(DataRequestMonitor<Object> rm) {
//selection context not used for the time being
fRegService.restoreDefaultGroups(null, rm);
}
};
fRegService.getExecutor().submit(queryRestore);
queryRestore.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS);
}
private void resetRegService() throws Throwable {
assert(fRegService instanceof GDBRegisters);
final GDBRegisters regManager = (GDBRegisters) fRegService;
Query<Object> queryReset = new Query<Object>() {
@Override
protected void execute(DataRequestMonitor<Object> rm) {
regManager.reset(rm);
}
};
regManager.getExecutor().submit(queryReset);
queryReset.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS);
}
private void saveRegGroups() throws Throwable {
assert(fRegService instanceof GDBRegisters);
final GDBRegisters regManager = (GDBRegisters) fRegService;
Query<Object> querySave = new Query<Object>() {
@Override
protected void execute(DataRequestMonitor<Object> rm) {
regManager.save();
rm.done();
}
};
regManager.getExecutor().submit(querySave);
querySave.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS);
}
private IRegisterDMData getRegisterData(final IRegisterDMContext registerDmc) throws Throwable {
Query<IRegisterDMData> registerDataQ = new Query<IRegisterDMData>() {
@Override
protected void execute(DataRequestMonitor<IRegisterDMData> rm) {
fRegService.getRegisterData(registerDmc, rm);
}
};
fRegService.getExecutor().submit(registerDataQ);
IRegisterDMData registerData = registerDataQ.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS);
assertNotNull(registerData);
return registerData;
}
private FormattedValueDMData getRegisterValue(final IRegisterDMContext registerDmc) throws Throwable {
Query<FormattedValueDMData> registerValueQ = new Query<FormattedValueDMData>() {
@Override
protected void execute(DataRequestMonitor<FormattedValueDMData> rm) {
FormattedValueDMContext valueDmc = new FormattedValueDMContext(fRegService, registerDmc, IFormattedValues.NATURAL_FORMAT);
fRegService.getFormattedExpressionValue(valueDmc, rm);
}
};
fRegService.getExecutor().submit(registerValueQ);
FormattedValueDMData registerValue = registerValueQ.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS);
assertNotNull(registerValue);
return registerValue;
}
private IRegisterGroupDMData getRegisterGroupData(final IRegisterGroupDMContext groupDmc) throws Throwable {
Query<IRegisterGroupDMData> groupDataQ = new Query<IRegisterGroupDMData>() {
@Override
protected void execute(DataRequestMonitor<IRegisterGroupDMData> rm) {
fRegService.getRegisterGroupData(groupDmc, rm);
}
};
fRegService.getExecutor().submit(groupDataQ);
IRegisterGroupDMData groupData = groupDataQ.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS);
assertNotNull(groupData);
return groupData;
}
private IRegisterGroupDMData[] getRegisterGroupsData(final IDMContext dmc) throws Throwable {
Query<IRegisterGroupDMData[]> groupDataQ = new Query<IRegisterGroupDMData[]>() {
@Override
protected void execute(DataRequestMonitor<IRegisterGroupDMData[]> rm) {
getRegisterGroupsData(dmc, rm);
}
};
fRegService.getExecutor().submit(groupDataQ);
IRegisterGroupDMData[] groupsData = groupDataQ.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS);
assertNotNull(groupsData);
return groupsData;
}
private void getRegisterGroupsData(final IDMContext dmc, final DataRequestMonitor<IRegisterGroupDMData[]> rm) {
assert (dmc != null);
final DsfExecutor executor = fRegService.getExecutor();
// First get all register group contexts, any register context can be used to resolve the container context
fRegService.getRegisterGroups(dmc, new DataRequestMonitor<IRegisterGroupDMContext[]>(executor, rm) {
@Override
protected void handleSuccess() {
final IRegisterGroupDMContext[] groupsCtx = getData();
assert (groupsCtx != null);
final IRegisterGroupDMData[] groupsData = new IRegisterGroupDMData[groupsCtx.length];
final CountingRequestMonitor crm = new CountingRequestMonitor(executor, rm) {
@Override
protected void handleCompleted() {
rm.setData(groupsData);
rm.done();
}
};
// Resolve all register group data
for (int i = 0; i < groupsCtx.length; i++) {
final int index = i;
fRegService.getRegisterGroupData(groupsCtx[index], new DataRequestMonitor<IRegisterGroupDMData>(executor, crm) {
@Override
protected void handleSuccess() {
groupsData[index] = getData();
crm.done();
}
});
}
crm.setDoneCount(groupsCtx.length);
}
});
}
private IRegisterDMContext[] getRegisters(int from, int to) throws Throwable {
MIStoppedEvent stoppedEvent = getInitialStoppedEvent();
IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0);
final IRegisterDMContext[] regDMCs = getAllRegisters(frameDmc);
// Shall not ask for more than is available
assertTrue(regDMCs.length > to);
// Retrieve the register range
final IRegisterDMContext[] regRange = Arrays.copyOfRange(regDMCs, from, to);
return regRange;
}
private IRegisterGroupDMContext addDefaultUserGroup() throws Throwable {
// Define a subset of registers to create a register group
final IRegisterDMContext[] regInGroup = getRegisters(0, 4);
// Request the addition of the new group
addGroup(GROUP_X, regInGroup);
// Retrieve the existing groups, expected and validated to 2
IRegisterGroupDMContext[] groups = getRegisterGroups(2);
// The groups are returned in reversed order to present latest created
// first i.e. index 0
// Our new group shall be at the top then i.e. 0
IFrameDMContext frameDmc = DMContexts.getAncestorOfType(regInGroup[0], IFrameDMContext.class);
CompositeDMContext compositeDmc = new CompositeDMContext(new IDMContext[]{groups[0], frameDmc});
IRegisterDMContext[] readRegisters = getRegisters(compositeDmc);
// Same objects and same order expected, although different parents
assertTrue(sameData(regInGroup, readRegisters, false));
return groups[0];
}
private IRegisterGroupDMContext[] addRegisterGroups(int numberOfNewGroups) throws Throwable {
// Define a subset of registers to associate to the new register groups
final IRegisterDMContext[] regInGroup = getRegisters(0, 4);
for (int i = 0; i < numberOfNewGroups; i++) {
String groupName = proposeGroupName();
addGroup(groupName, regInGroup);
}
// Expected number of groups = Root + numberofNewGroups
return getRegisterGroups(1 + numberOfNewGroups);
}
/**
* Check the register Data entry names are in the same order
*/
private boolean sameRegisterNames(IRegisterDMData[] regNames1, IRegisterDMData[] regNames2) {
boolean same = false;
if (regNames1.length == regNames2.length) {
for (int i = 0; i < regNames1.length; i++) {
if (regNames1[i].getName().equals(regNames2[i].getName())) {
continue;
} else {
// Found a different name, Not the same !!
return false;
}
}
// All names matched !!
return true;
}
return same;
}
private boolean sameData(IRegisterDMContext[] regArrOne, IRegisterDMContext[] regArrTwo, boolean sameParentGroup) throws Throwable {
if (regArrOne.length != regArrTwo.length) {
return false;
}
for (int i=0; i< regArrOne.length; i++) {
if (sameParentGroup) {
//same group parent expected
final IRegisterGroupDMContext parentGroupOne = DMContexts.getAncestorOfType(regArrOne[i], IRegisterGroupDMContext.class);
final IRegisterGroupDMContext parentGroupTwo = DMContexts.getAncestorOfType(regArrTwo[i], IRegisterGroupDMContext.class);
if(!parentGroupOne.equals(parentGroupTwo)) {
return false;
}
}
//same data
IRegisterDMData dataOne = getRegisterData(regArrOne[i]);
IRegisterDMData dataTwo = getRegisterData(regArrTwo[i]);
if (!dataOne.getName().equals(dataTwo.getName())) {
return false;
}
//same value
FormattedValueDMData valueOne = getRegisterValue(regArrOne[i]);
FormattedValueDMData valueTwo = getRegisterValue(regArrTwo[i]);
if (!valueOne.getFormattedValue().equals(valueTwo.getFormattedValue())) {
return false;
}
}
//All data is the same
return true;
}
/**
* Resolve from proposed name to group sequence number e.g Group_5 -> 5
*/
private int resolveGroupNameSequence(String groupName) {
int sequence = 0;
String[] strSequence = groupName.split("_");
assertEquals(2, strSequence.length);
sequence = Integer.parseInt(strSequence[1]);
return sequence;
}
}