以下单元测试旨在验证是否已根据传递给构造函数的数据正确解析嵌套的JNA数据结构。单元测试正确检查JNA何时初始化“父”结构。但是,测试嵌套结构的成员变量时测试失败。我怀疑问题是单元测试设置指向包含嵌套结构数据的内存位置的指针。也许Java指针并没有一对一地转换为C如何做到这一点。
我尝试将WlanBssEntry放在WlanBssEntry []的开头,但是它没有提供与当前实现的结果不同的结果。当我尝试连续放置它时,我没有打电话给pointerToMem.setPointer(8, ...)
这是单元测试:
package com.sevensignal.EyeQAgent.Util.win32.struct;
import com.sevensignal.EyeQAgent.Models.Platform;
import com.sevensignal.EyeQAgent.Util.Utils;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.WinDef;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.*;
import static org.junit.Assume.assumeThat;
@RunWith(PowerMockRunner.class)
@PrepareForTest({Utils.class})
public class WlanBssListTest {
Pointer pWlanBssList;
private final static long OFFSET_TO_FIRST_WLAN_BSS_ENTRY = 1024;
@Before
public void setUp() {
assumeThat(Utils.getPlatform(), equalTo(Platform.WINDOWS));
pWlanBssList = allocateMemory(65536);
initWlanBssEntryMemory(pWlanBssList, OFFSET_TO_FIRST_WLAN_BSS_ENTRY, 50);
initWlanBssListMemory(pWlanBssList, 12, 1, OFFSET_TO_FIRST_WLAN_BSS_ENTRY);
}
@Test
public void shouldSetTotalSize() {
WlanBssList subject = new WlanBssList(pWlanBssList);
assertEquals("should set total size", new WinDef.DWORD(12), subject.dwTotalSize);
}
@Test
public void shouldSetNumberOfItems() {
WlanBssList subject = new WlanBssList(pWlanBssList);
assertEquals("should set number of items", new WinDef.DWORD(1), subject.dwNumberOfItems);
}
@Test
public void shouldInitWlanBssEntries() {
WlanBssList subject = new WlanBssList(pWlanBssList);
assertEquals("should init WLAN BSS Entries", 1, subject.wlanBssEntries.length);
assertEquals("should init WLAN BSS Entry data struct", new WinDef.LONG(50), subject.wlanBssEntries[0].uPhyId);
}
@Test
public void shouldInitWlanBssEntriesWhenNoEntriesExist() {
pWlanBssList.setInt(4, 0);
WlanBssList subject = new WlanBssList(pWlanBssList);
assertEquals("should init WLAN BSS Entries when no entries exist", 0, subject.wlanBssEntries.length);
}
private Pointer allocateMemory(long size) {
return new Memory(size).share(0);
}
private void initWlanBssListMemory(Pointer pointerToMem, int dwTotalSize, int dwNumberOfItems, long offsetToWlanBssEntry) {
pointerToMem.setInt(0, dwTotalSize);
pointerToMem.setInt(4, dwNumberOfItems);
pointerToMem.setPointer(8, pointerToMem.share(offsetToWlanBssEntry));
}
private void initWlanBssEntryMemory(Pointer pointerToMem, long offsetToWlanBssEntry, long uPhyId) {
final int PHY_ID_OFFSET = 40;
pointerToMem.setLong(offsetToWlanBssEntry + 0, 3);
pointerToMem.setByte(offsetToWlanBssEntry + 8, (byte)'T');
pointerToMem.setByte(offsetToWlanBssEntry + 9, (byte)'S');
pointerToMem.setByte(offsetToWlanBssEntry + 10, (byte)'T');
pointerToMem.setLong(offsetToWlanBssEntry + PHY_ID_OFFSET, uPhyId);
}
}
这是WlanBssList类,它是最高级别的结构:
package com.sevensignal.EyeQAgent.Util.win32.struct;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.WinDef.DWORD;
import java.util.Arrays;
import java.util.List;
public class WlanBssList extends Structure {
public DWORD dwTotalSize;
public DWORD dwNumberOfItems;
public WlanBssEntry[] wlanBssEntries;
public static class ByReference extends WlanBssList implements Structure.ByReference
{
public ByReference()
{
}
public ByReference(Pointer p)
{
super(p);
}
}
public WlanBssList() {
wlanBssEntries = new WlanBssEntry[1];
}
public WlanBssList(Pointer p) {
super(p);
dwTotalSize = new DWORD(p.getInt(0));
dwNumberOfItems = new DWORD(p.getInt(4));
if(dwNumberOfItems.intValue() > 0) {
wlanBssEntries = new WlanBssEntry[dwNumberOfItems.intValue()];
readField("wlanBssEntries");
} else {
wlanBssEntries = new WlanBssEntry[0];
}
}
@Override
protected List<String> getFieldOrder() {
return Arrays.asList("dwTotalSize", "dwNumberOfItems", "wlanBssEntries");
}
}
这些是嵌套结构:
package com.sevensignal.EyeQAgent.Util.win32.struct;
import com.sevensignal.EyeQAgent.Models.InformationElementGetter;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.WinDef.LONG;
import com.sun.jna.platform.win32.WinDef.ULONG;
import com.sun.jna.platform.win32.WinDef.ULONGLONG;
import com.sun.jna.platform.win32.WinDef.USHORT;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.NoArgsConstructor;
import java.util.Arrays;
import java.util.List;
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class WlanBssEntry extends Structure implements InformationElementGetter {
public DOT11_SSID dot11Ssid;
public ULONG uPhyId;
public DOT11_MAC_ADDRESS dot11Bssid;
public int dot11BssType;
public int dot11BssPhyType;
public LONG lRssi;
public ULONG uLinkQuality;
public boolean bInRegDomain;
public USHORT usBeaconPeriod;
public ULONGLONG ullTimestamp;
public ULONGLONG ullHostTimestamp;
public USHORT usCapabilityInformation;
public ULONG ulChCenterFrequency;
public WlanRateSet wlanRateSet;
public ULONG ulIeOffset;
public ULONG ulIeSize;
@Override
protected List<String> getFieldOrder()
{
return Arrays.asList("dot11Ssid",
"uPhyId",
"dot11Bssid",
"dot11BssType",
"dot11BssPhyType",
"lRssi",
"uLinkQuality",
"bInRegDomain",
"usBeaconPeriod",
"ullTimestamp",
"ullHostTimestamp",
"usCapabilityInformation",
"ulChCenterFrequency",
"wlanRateSet",
"ulIeOffset",
"ulIeSize");
}
public byte[] getInformationElement() {
return this.getPointer()
.getByteArray(this.ulIeOffset.intValue(), this.ulIeSize.intValue());
}
}
package com.sevensignal.EyeQAgent.Util.win32.struct;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.WinDef.*;
import java.util.Arrays;
import java.util.List;
public class DOT11_SSID extends Structure
{
public static class ByReference extends DOT11_SSID implements Structure.ByReference
{
}
public static int DOT11_SSID_MAX_LENGTH = 32;
/**
* The length, in bytes, of the ucSSID array.
*/
public ULONG uSSIDLength;
/**
* The SSID. DOT11_SSID_MAX_LENGTH is set to 32.
*/
public byte[] ucSSID;
public DOT11_SSID()
{
ucSSID = new byte[DOT11_SSID_MAX_LENGTH];
}
@Override
protected List<String> getFieldOrder()
{
return Arrays.asList("uSSIDLength", "ucSSID");
}
@Override
public String toString() {
if(uSSIDLength != null) {
int ssidArrayLength = uSSIDLength.intValue();
if (ssidArrayLength > DOT11_SSID_MAX_LENGTH) {
ssidArrayLength = DOT11_SSID_MAX_LENGTH;
}
return new String(Arrays.copyOfRange(ucSSID, 0, ssidArrayLength));
} else {
return "";
}
}
}
单元测试的结果是:
java.lang.AssertionError: should init WLAN BSS Entry data struct
Expected :50
Actual :0
失败的断言是:
assertEquals("should init WLAN BSS Entry data struct", new WinDef.LONG(50), subject.wlanBssEntries[0].uPhyId);
我修复了失败的单元测试。在分配的内存中初始化数据时有2个问题。
首先,uPhyId
的偏移量减少了4,因为我认为dot11Ssid
的长度为8而不是4。
其次,第一个WlanBssEntry
的记忆位于WlanBssList
结构的偏移量为8。
最后,我改变了wlanBssEntries
的初始化方式,以保证WlanBssEntry
实例数组在内存中连续出现。即使没有这种改变,我的单元测试也通过但根据@Daniel Widdis建议以及一些在线文档,我认为最好遵循建议的程序。它初始化如下:
wlanBssEntries = (WlanBssEntry[])(new WlanBssEntry()).toArray(dwNumberOfItems.intValue());
以下是传递单元测试的代码:
package com.sevensignal.EyeQAgent.Util.win32.struct;
import com.sevensignal.EyeQAgent.Models.Platform;
import com.sevensignal.EyeQAgent.Util.Utils;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.WinDef;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.*;
import static org.junit.Assume.assumeThat;
@RunWith(PowerMockRunner.class)
@PrepareForTest({Utils.class})
public class WlanBssListTest {
Pointer pWlanBssList;
private final static long OFFSET_TO_FIRST_WLAN_BSS_ENTRY = 8;
private final static int PHY_ID_OFFSET = 36;
private final static int WLAN_BSS_ENTRY_LENGTH = 360;
@Before
public void setUp() {
assumeThat(Utils.getPlatform(), equalTo(Platform.WINDOWS));
pWlanBssList = allocateMemory(65536);
}
@Test
public void shouldSetTotalSize() {
initWlanBssListMemory(pWlanBssList, 12, 1);
WlanBssList subject = new WlanBssList(pWlanBssList);
assertEquals("should set total size", new WinDef.DWORD(12), subject.dwTotalSize);
}
@Test
public void shouldSetNumberOfItems() {
initWlanBssListMemory(pWlanBssList, 12, 1);
WlanBssList subject = new WlanBssList(pWlanBssList);
assertEquals("should set number of items", new WinDef.DWORD(1), subject.dwNumberOfItems);
}
@Test
public void shouldInitWlanBssEntries() {
initWlanBssListMemory(pWlanBssList, 12, 1);
WlanBssList subject = new WlanBssList(pWlanBssList);
assertArrayEquals("should init SSID entry in WLAN BSS Entry data struct",
new byte[]{
(byte)'I', (byte)'D', (byte)'1', (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00
},
subject.wlanBssEntries[0].dot11Ssid.ucSSID);
assertEquals("should init WLAN BSS Entry data struct", new WinDef.LONG(51), subject.wlanBssEntries[0].uPhyId);
}
@Test
public void shouldInitTwoWlanBssEntries() {
initWlanBssListMemory(pWlanBssList, 12, 2);
WlanBssList subject = new WlanBssList(pWlanBssList);
assertArrayEquals("should init SSID entry in WLAN BSS Entry data struct",
new byte[]{
(byte)'I', (byte)'D', (byte)'1', (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00
},
subject.wlanBssEntries[0].dot11Ssid.ucSSID);
assertEquals("should init WLAN BSS Entry data struct", new WinDef.LONG(51), subject.wlanBssEntries[0].uPhyId);
assertArrayEquals("should init SSID entry in WLAN BSS Entry data struct",
new byte[]{
(byte)'I', (byte)'D', (byte)'2', (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00
},
subject.wlanBssEntries[1].dot11Ssid.ucSSID);
assertEquals("should init WLAN BSS Entry data struct", new WinDef.LONG(0xC2), subject.wlanBssEntries[1].uPhyId);
}
@Test
public void shouldInitWlanBssEntriesWhenNoEntriesExist() {
initWlanBssListMemory(pWlanBssList, 12, 0);
WlanBssList subject = new WlanBssList(pWlanBssList);
assertEquals("should init WLAN BSS Entries when no entries exist", 0, subject.wlanBssEntries.length);
}
private Pointer allocateMemory(long size) {
return new Memory(size).share(0);
}
private void initWlanBssListMemory(Pointer pointerToMem, int dwTotalSize, int dwNumberOfItems) {
pointerToMem.setInt(0, dwTotalSize);
pointerToMem.setInt(4, dwNumberOfItems);
initWlanBssEntryMemory(pointerToMem);
}
private void initWlanBssEntryMemory(Pointer pointerToWlanBssEntryMem) {
pointerToWlanBssEntryMem.setLong(OFFSET_TO_FIRST_WLAN_BSS_ENTRY + 0, 3);
pointerToWlanBssEntryMem.setByte(OFFSET_TO_FIRST_WLAN_BSS_ENTRY + 4, (byte)'I');
pointerToWlanBssEntryMem.setByte(OFFSET_TO_FIRST_WLAN_BSS_ENTRY + 5, (byte)'D');
pointerToWlanBssEntryMem.setByte(OFFSET_TO_FIRST_WLAN_BSS_ENTRY + 6, (byte)'1');
pointerToWlanBssEntryMem.setMemory(OFFSET_TO_FIRST_WLAN_BSS_ENTRY + 7, 29, (byte)0);
pointerToWlanBssEntryMem.setLong(OFFSET_TO_FIRST_WLAN_BSS_ENTRY + PHY_ID_OFFSET, 51);
pointerToWlanBssEntryMem.setLong(OFFSET_TO_FIRST_WLAN_BSS_ENTRY + WLAN_BSS_ENTRY_LENGTH + 0, 3);
pointerToWlanBssEntryMem.setByte(OFFSET_TO_FIRST_WLAN_BSS_ENTRY + WLAN_BSS_ENTRY_LENGTH + 4, (byte)'I');
pointerToWlanBssEntryMem.setByte(OFFSET_TO_FIRST_WLAN_BSS_ENTRY + WLAN_BSS_ENTRY_LENGTH + 5, (byte)'D');
pointerToWlanBssEntryMem.setByte(OFFSET_TO_FIRST_WLAN_BSS_ENTRY + WLAN_BSS_ENTRY_LENGTH + 6, (byte)'2');
pointerToWlanBssEntryMem.setMemory(OFFSET_TO_FIRST_WLAN_BSS_ENTRY + WLAN_BSS_ENTRY_LENGTH + 7, 29, (byte)0);
pointerToWlanBssEntryMem.setLong(OFFSET_TO_FIRST_WLAN_BSS_ENTRY + WLAN_BSS_ENTRY_LENGTH + PHY_ID_OFFSET, 0xC2);
}
}