Неожиданная ошибка при модульном тестировании вложенных объектов структуры JNA - PullRequest
1 голос
/ 27 марта 2019

Следующий модульный тест предназначен для проверки правильности анализа вложенной структуры данных JNA на основе данных, которые передаются в конструктор. Модульный тест правильно проверяет, когда JNA инициализирует «родительскую» структуру. Однако тест не пройден при тестировании переменных-членов вложенной структуры. Я подозреваю, что проблема заключается в том, что модульный тест устанавливает указатель на область памяти, которая содержит данные для вложенной структуры. Возможно, Java Pointer не переводит один к одному, как это делает 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);

1 Ответ

1 голос
/ 28 марта 2019

Я исправил провал модульного теста. Было 2 проблемы, когда данные были инициализированы в выделенной памяти.

Прежде всего, смещение в uPhyId было отключено на 4, потому что я думал, что поле длины dot11Ssid было 8, а не 4.

Во-вторых, память для первого WlanBssEntry расположена со смещением 8 в структуре WlanBssList.

Наконец, я изменил способ инициализации 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);
    }
}
...