diff --git a/etc/visual-studio/UnitTests.vcxproj b/etc/visual-studio/UnitTests.vcxproj
index 2dba8d3e2..0dc711ee5 100644
--- a/etc/visual-studio/UnitTests.vcxproj
+++ b/etc/visual-studio/UnitTests.vcxproj
@@ -163,6 +163,7 @@
+
@@ -177,6 +178,7 @@
+
diff --git a/etc/visual-studio/UnitTests.vcxproj.filters b/etc/visual-studio/UnitTests.vcxproj.filters
index 1b018e3bc..375ac3b9f 100644
--- a/etc/visual-studio/UnitTests.vcxproj.filters
+++ b/etc/visual-studio/UnitTests.vcxproj.filters
@@ -21,6 +21,9 @@
Source Files
+
+ Source Files
+
Source Files
@@ -56,6 +59,9 @@
+
+ Header Files
+
Header Files
diff --git a/src/core/common/debug.hpp b/src/core/common/debug.hpp
index 94cd75318..aa9b21b34 100644
--- a/src/core/common/debug.hpp
+++ b/src/core/common/debug.hpp
@@ -51,6 +51,10 @@
(RtlAssert( #exp, __FILE__, __LINE__, NULL ),FALSE) : \
TRUE)
+#elif defined(_WIN32)
+
+#include
+
#else
#define assert(cond) \
diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp
index df9a3f81f..28e65ccc1 100644
--- a/src/core/mac/mac.cpp
+++ b/src/core/mac/mac.cpp
@@ -1154,6 +1154,10 @@ void Mac::ReceiveDoneTask(Frame *aFrame, ThreadError aError)
mPcapCallback(aFrame, mPcapCallbackContext);
}
+ // Ensure we have a valid frame before attempting to read any contents of
+ // the buffer received from the radio.
+ SuccessOrExit(error = aFrame->ValidatePsdu());
+
aFrame->GetSrcAddr(srcaddr);
neighbor = mMle.GetNeighbor(srcaddr);
diff --git a/src/core/mac/mac_frame.cpp b/src/core/mac/mac_frame.cpp
index 89a19cc61..30ea42ece 100644
--- a/src/core/mac/mac_frame.cpp
+++ b/src/core/mac/mac_frame.cpp
@@ -58,7 +58,7 @@ ThreadError Frame::InitMacHeader(uint16_t aFcf, uint8_t aSecurityControl)
// Sequence Number
length += kDsnSize;
- // Destinatino PAN + Address
+ // Destinatinon PAN + Address
switch (aFcf & Frame::kFcfDstAddrMask)
{
case Frame::kFcfDstAddrNone:
@@ -145,6 +145,125 @@ ThreadError Frame::InitMacHeader(uint16_t aFcf, uint8_t aSecurityControl)
return kThreadError_None;
}
+ThreadError Frame::ValidatePsdu(void)
+{
+ ThreadError error = kThreadError_Parse;
+ uint8_t offset = 0;
+ uint16_t fcf;
+ uint8_t footerLength = kFcsSize;
+
+ VerifyOrExit((offset += kFcfSize + kDsnSize) <= GetPsduLength(),);
+ fcf = static_cast((GetPsdu()[1] << 8) | GetPsdu()[0]);
+
+ // Destinatinon PAN + Address
+ switch (fcf & Frame::kFcfDstAddrMask)
+ {
+ case Frame::kFcfDstAddrNone:
+ break;
+
+ case Frame::kFcfDstAddrShort:
+ offset += sizeof(PanId) + sizeof(ShortAddress);
+ break;
+
+ case Frame::kFcfDstAddrExt:
+ offset += sizeof(PanId) + sizeof(ExtAddress);
+ break;
+
+ default:
+ goto exit;
+ }
+
+ // Source PAN + Address
+ switch (fcf & Frame::kFcfSrcAddrMask)
+ {
+ case Frame::kFcfSrcAddrNone:
+ break;
+
+ case Frame::kFcfSrcAddrShort:
+ if ((fcf & Frame::kFcfPanidCompression) == 0)
+ {
+ offset += sizeof(PanId);
+ }
+
+ offset += sizeof(ShortAddress);
+ break;
+
+ case Frame::kFcfSrcAddrExt:
+ if ((fcf & Frame::kFcfPanidCompression) == 0)
+ {
+ offset += sizeof(PanId);
+ }
+
+ offset += sizeof(ExtAddress);
+ break;
+
+ default:
+ goto exit;
+ }
+
+ // Security Header
+ if (fcf & Frame::kFcfSecurityEnabled)
+ {
+ VerifyOrExit(offset <= GetPsduLength(),);
+ uint8_t secControl = GetPsdu()[offset];
+
+ switch (secControl & kKeyIdModeMask)
+ {
+ case kKeyIdMode0:
+ offset += kKeySourceSizeMode0;
+ break;
+
+ case kKeyIdMode1:
+ offset += kKeySourceSizeMode1 + kKeyIndexSize;
+ break;
+
+ case kKeyIdMode2:
+ offset += kKeySourceSizeMode2 + kKeyIndexSize;
+ break;
+
+ case kKeyIdMode3:
+ offset += kKeySourceSizeMode3 + kKeyIndexSize;
+ break;
+ }
+
+ switch (secControl & kSecLevelMask)
+ {
+ case kSecNone:
+ case kSecEnc:
+ footerLength += kMic0Size;
+ break;
+
+ case kSecMic32:
+ case kSecEncMic32:
+ footerLength += kMic32Size;
+ break;
+
+ case kSecMic64:
+ case kSecEncMic64:
+ footerLength += kMic64Size;
+ break;
+
+ case kSecMic128:
+ case kSecEncMic128:
+ footerLength += kMic128Size;
+ break;
+ }
+ }
+
+ // Command ID
+ if ((fcf & kFcfFrameTypeMask) == kFcfFrameMacCmd)
+ {
+ offset += kCommandIdSize;
+ }
+
+ VerifyOrExit((offset + footerLength) <= GetPsduLength(),);
+
+ error = kThreadError_None;
+
+exit:
+ return error;
+}
+
uint8_t Frame::GetType(void)
{
return GetPsdu()[0] & Frame::kFcfFrameTypeMask;
diff --git a/src/core/mac/mac_frame.hpp b/src/core/mac/mac_frame.hpp
index 3352b0f62..b70240853 100644
--- a/src/core/mac/mac_frame.hpp
+++ b/src/core/mac/mac_frame.hpp
@@ -251,6 +251,15 @@ public:
*/
ThreadError InitMacHeader(uint16_t aFcf, uint8_t aSecCtl);
+ /**
+ * This method validates the frame.
+ *
+ * @retval kThreadError_None Successfully parsed the MAC header.
+ * @retval kThreadError_Parse Failed to parse through the MAC header.
+ *
+ */
+ ThreadError ValidatePsdu(void);
+
/**
* This method returns the IEEE 802.15.4 Frame Type.
*
diff --git a/src/core/thread/lowpan.cpp b/src/core/thread/lowpan.cpp
index 9e6d7fae8..b1ad5b9cc 100644
--- a/src/core/thread/lowpan.cpp
+++ b/src/core/thread/lowpan.cpp
@@ -65,6 +65,8 @@ ThreadError Lowpan::CopyContext(const Context &aContext, Ip6::Address &aAddress)
ThreadError Lowpan::ComputeIid(const Mac::Address &aMacAddr, const Context &aContext, Ip6::Address &aIpAddress)
{
+ ThreadError error = kThreadError_None;
+
switch (aMacAddr.mLength)
{
case 2:
@@ -79,7 +81,7 @@ ThreadError Lowpan::ComputeIid(const Mac::Address &aMacAddr, const Context &aCon
break;
default:
- assert(false);
+ ExitNow(error = kThreadError_Parse);
}
if (aContext.mPrefixLength > 64)
@@ -91,7 +93,8 @@ ThreadError Lowpan::ComputeIid(const Mac::Address &aMacAddr, const Context &aCon
}
}
- return kThreadError_None;
+exit:
+ return error;
}
int Lowpan::CompressSourceIid(const Mac::Address &aMacAddr, const Ip6::Address &aIpAddr, const Context &aContext,
@@ -716,7 +719,7 @@ int Lowpan::DecompressBaseHeader(Ip6::Header &ip6Header, const Mac::Address &aMa
break;
case kHcDstAddrMode3:
- ComputeIid(aMacDest, dstContext, ip6Header.GetDestination());
+ SuccessOrExit(error = ComputeIid(aMacDest, dstContext, ip6Header.GetDestination()));
break;
}
@@ -946,6 +949,8 @@ int Lowpan::Decompress(Message &aMessage, const Mac::Address &aMacSource, const
uint16_t compressedLength = 0;
uint16_t currentOffset = aMessage.GetOffset();
+ VerifyOrExit(aBufLen >= 2, error = kThreadError_Parse);
+
compressed = (((static_cast(cur[0]) << 8) | cur[1]) & kHcNextHeader) != 0;
VerifyOrExit((rval = DecompressBaseHeader(ip6Header, aMacSource, aMacDest, aBuf)) >= 0,
diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp
index 1d3ad285f..a9fb3bd91 100644
--- a/src/core/thread/mesh_forwarder.cpp
+++ b/src/core/thread/mesh_forwarder.cpp
@@ -1413,15 +1413,18 @@ void MeshForwarder::HandleReceivedFrame(Mac::Frame &aFrame)
switch (aFrame.GetType())
{
case Mac::Frame::kFcfFrameData:
- if (reinterpret_cast(payload)->IsMeshHeader())
+ if (payloadLength >= sizeof(Lowpan::MeshHeader) &&
+ reinterpret_cast(payload)->IsMeshHeader())
{
HandleMesh(payload, payloadLength, messageInfo);
}
- else if (reinterpret_cast(payload)->IsFragmentHeader())
+ else if (payloadLength >= sizeof(Lowpan::FragmentHeader) &&
+ reinterpret_cast(payload)->IsFragmentHeader())
{
HandleFragment(payload, payloadLength, macSource, macDest, messageInfo);
}
- else if (Lowpan::Lowpan::IsLowpanHc(payload))
+ else if (payloadLength >= 1 &&
+ Lowpan::Lowpan::IsLowpanHc(payload))
{
HandleLowpanHC(payload, payloadLength, macSource, macDest, messageInfo);
}
@@ -1564,6 +1567,8 @@ void MeshForwarder::HandleFragment(uint8_t *aFrame, uint8_t aFrameLength,
aFrame += headerLength;
aFrameLength -= static_cast(headerLength);
+ VerifyOrExit(datagramLength >= message->GetOffset() + aFrameLength, error = kThreadError_Parse);
+
SuccessOrExit(error = message->SetLength(datagramLength));
message->SetDatagramTag(datagramTag);
@@ -1718,7 +1723,7 @@ void MeshForwarder::HandleDataRequest(const Mac::Address &aMacSource, const Thre
// Security Check: only process secure Data Poll frames.
VerifyOrExit(aMessageInfo.mLinkSecurity, ;);
- assert(mMle.GetDeviceState() != Mle::kDeviceStateDetached);
+ VerifyOrExit(mMle.GetDeviceState() != Mle::kDeviceStateDetached, ;);
VerifyOrExit((child = mMle.GetChild(aMacSource)) != NULL, ;);
child->mLastHeard = Timer::GetNow();
diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am
index fad64a538..f4586ec50 100644
--- a/tests/unit/Makefile.am
+++ b/tests/unit/Makefile.am
@@ -33,6 +33,7 @@ include $(abs_top_nlbuild_autotools_dir)/automake/pre.am
# since they are not part of the package.
#
noinst_HEADERS = \
+ test_platform.h \
test_util.h \
test_util.hpp \
test_vector.h \
@@ -73,6 +74,7 @@ endif # OPENTHREAD_ENABLE_BUILTIN_MBEDTLS
check_PROGRAMS = \
test-aes \
+ test-fuzz \
test-hmac-sha256 \
test-lowpan \
test-link-quality \
@@ -118,6 +120,9 @@ TESTS_ENVIRONMENT = \
test_aes_LDADD = $(COMMON_LDADD)
test_aes_SOURCES = test_platform.cpp test_aes.cpp
+test_fuzz_LDADD = $(COMMON_LDADD)
+test_fuzz_SOURCES = test_platform.cpp test_fuzz.cpp
+
test_hmac_sha256_LDADD = $(COMMON_LDADD)
test_hmac_sha256_SOURCES = test_platform.cpp test_hmac_sha256.cpp
diff --git a/tests/unit/test_fuzz.cpp b/tests/unit/test_fuzz.cpp
new file mode 100644
index 000000000..5d6598d4f
--- /dev/null
+++ b/tests/unit/test_fuzz.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2016, The OpenThread Authors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "test_platform.h"
+//#include
+
+//#define DBG_FUZZ 1
+
+bool g_fRadioEnabled = false;
+uint8_t g_RecvChannel = 0;
+uint8_t g_TransmitPsdu[128];
+RadioPacket g_TransmitRadioPacket;
+bool g_fTransmit = false;
+
+ThreadError testFuzzRadioEnable(otInstance *)
+{
+#ifdef DBG_FUZZ
+ Log("Radio enabled");
+#endif
+ g_fRadioEnabled = true;
+ return kThreadError_None;
+}
+
+ThreadError testFuzzRadioDisable(otInstance *)
+{
+#ifdef DBG_FUZZ
+ Log("Radio disabled");
+#endif
+ g_fRadioEnabled = false;
+ return kThreadError_None;
+}
+
+ThreadError testFuzzRadioReceive(otInstance *, uint8_t aChannel)
+{
+#ifdef DBG_FUZZ
+ Log("==> receive");
+#endif
+ g_RecvChannel = aChannel;
+ return kThreadError_None;
+}
+
+ThreadError testFuzzRadioTransmit(otInstance *)
+{
+#ifdef DBG_FUZZ
+ Log("==> transmit");
+#endif
+ g_fTransmit = true;
+ return kThreadError_None;
+}
+
+RadioPacket *testFuzztRadioGetTransmitBuffer(otInstance *)
+{
+ return &g_TransmitRadioPacket;
+}
+
+void TestFuzz(uint32_t aSeconds)
+{
+ // Set the radio capabilities to disable any Mac related timer dependencies
+ g_testPlatRadioCaps = (otRadioCaps)(kRadioCapsAckTimeout | kRadioCapsTransmitRetries);
+
+ // Set the platform function pointers
+ g_TransmitRadioPacket.mPsdu = g_TransmitPsdu;
+ g_testPlatRadioEnable = testFuzzRadioEnable;
+ g_testPlatRadioDisable = testFuzzRadioDisable;
+ g_testPlatRadioReceive = testFuzzRadioReceive;
+ g_testPlatRadioTransmit = testFuzzRadioTransmit;
+ g_testPlatRadioGetTransmitBuffer = testFuzztRadioGetTransmitBuffer;
+
+ // Initialize our timing variables
+ uint32_t tStart = otPlatAlarmGetNow();
+ uint32_t tEnd = tStart + (aSeconds * 1000);
+
+ otInstance *aInstance;
+
+#ifdef _WIN32
+ uint32_t seed = (uint32_t)time(NULL);
+ srand(seed);
+ Log("Initialized seed = 0x%X", seed);
+#endif
+
+#ifdef OPENTHREAD_MULTIPLE_INSTANCE
+ uint64_t otInstanceBufferLength = 0;
+ uint8_t *otInstanceBuffer = NULL;
+
+ // Call to query the buffer size
+ (void)otInstanceInit(NULL, &otInstanceBufferLength);
+
+ // Call to allocate the buffer
+ otInstanceBuffer = (uint8_t *)malloc(otInstanceBufferLength);
+ VerifyOrQuit(otInstanceBuffer != NULL, "Failed to allocate otInstance");
+ memset(&otInstanceBuffer, 0, otInstanceBufferLength);
+
+ // Initialize Openthread with the buffer
+ aInstance = otInstanceInit(otInstanceBuffer, &otInstanceBufferLength);
+#else
+ aInstance = otInstanceInit();
+#endif
+
+ VerifyOrQuit(aInstance != NULL, "Failed to initialize otInstance");
+
+ // Start the Thread network
+ otSetPanId(aInstance, (otPanId)0xFACE);
+ otInterfaceUp(aInstance);
+ otThreadStart(aInstance);
+
+ uint32_t countRecv = 0;
+
+ while (otPlatAlarmGetNow() < tEnd)
+ {
+ otProcessQueuedTasklets(aInstance);
+
+ if (g_testPlatAlarmSet && otPlatAlarmGetNow() >= g_testPlatAlarmNext)
+ {
+ g_testPlatAlarmSet = false;
+ otPlatAlarmFired(aInstance);
+ }
+
+ if (g_fRadioEnabled)
+ {
+ if (g_fTransmit)
+ {
+ g_fTransmit = false;
+ otPlatRadioTransmitDone(aInstance, true, kThreadError_None);
+#ifdef DBG_FUZZ
+ Log("<== transmit");
+#endif
+ }
+
+ if (g_RecvChannel != 0)
+ {
+ uint8_t fuzzRecvBuff[128];
+ RadioPacket fuzzPacket;
+
+ // Initialize the radio packet with a random length
+ memset(&fuzzPacket, 0, sizeof(fuzzPacket));
+ fuzzPacket.mPsdu = fuzzRecvBuff;
+ fuzzPacket.mChannel = g_RecvChannel;
+ fuzzPacket.mLength = (uint8_t)(otPlatRandomGet() % 127);
+
+ // Populate the length with random
+ for (uint8_t i = 0; i < fuzzPacket.mLength; i++)
+ {
+ fuzzRecvBuff[i] = (uint8_t)otPlatRandomGet();
+ }
+
+ // Clear the global flag
+ g_RecvChannel = 0;
+
+ // Indicate the receive complete
+ otPlatRadioReceiveDone(aInstance, &fuzzPacket, kThreadError_None);
+
+ countRecv++;
+#ifdef DBG_FUZZ
+ Log("<== receive (%llu, %u bytes)", countRecv, fuzzPacket.mLength);
+#endif
+
+ // Hack to get a receive poll immediately
+ otSetChannel(aInstance, 11);
+ }
+ }
+ }
+
+ Log("%u packets received", countRecv);
+
+ // Clean up the instance
+ otInstanceFinalize(aInstance);
+
+#ifdef OPENTHREAD_MULTIPLE_INSTANCE
+ free(otInstanceBuffer);
+#endif
+}
+
+#ifdef ENABLE_TEST_MAIN
+int main(void)
+{
+ TestFuzz(0); // For the time being, don't actually fuzz, just validate we can start and clean up
+ printf("All tests passed\n");
+ return 0;
+}
+#endif
diff --git a/tests/unit/test_platform.cpp b/tests/unit/test_platform.cpp
index cd1c6d2bd..a9f1508c6 100644
--- a/tests/unit/test_platform.cpp
+++ b/tests/unit/test_platform.cpp
@@ -26,38 +26,54 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
+#include "test_platform.h"
+
#if _WIN32
-#define _CRT_SECURE_NO_WARNINGS
-#include
+__forceinline int gettimeofday(struct timeval *tv, struct timezone *)
+{
+ DWORD tick = GetTickCount();
+ tv->tv_sec = (long)(tick / 1000);
+ tv->tv_usec = (long)(tick * 1000);
+ return 0;
+}
+#else
+#include
#endif
-#include
+bool g_testPlatAlarmSet = false;
+uint32_t g_testPlatAlarmNext = 0;
+testPlatAlarmStop g_testPlatAlarmStop = NULL;
+testPlatAlarmStartAt g_testPlatAlarmStartAt = NULL;
+testPlatAlarmGetNow g_testPlatAlarmGetNow = NULL;
-#include
-#include
-#include
-#include
-#include
-#include
+otRadioCaps g_testPlatRadioCaps = kRadioCapsNone;
+testPlatRadioSetPanId g_testPlatRadioSetPanId = NULL;
+testPlatRadioSetExtendedAddress g_testPlatRadioSetExtendedAddress = NULL;
+testPlatRadioEnable g_testPlatRadioEnable = NULL;
+testPlatRadioDisable g_testPlatRadioDisable = NULL;
+testPlatRadioSetShortAddress g_testPlatRadioSetShortAddress = NULL;
+testPlatRadioReceive g_testPlatRadioReceive = NULL;
+testPlatRadioTransmit g_testPlatRadioTransmit = NULL;
+testPlatRadioGetTransmitBuffer g_testPlatRadioGetTransmitBuffer = NULL;
-#include
-#include
-#include
-
-enum
+void testPlatResetToDefaults(void)
{
- kCallCountIndexAlarmStop = 0,
- kCallCountIndexAlarmStart,
- kCallCountIndexTimerHandler,
+ g_testPlatAlarmSet = false;
+ g_testPlatAlarmNext = 0;
+ g_testPlatAlarmStop = NULL;
+ g_testPlatAlarmStartAt = NULL;
+ g_testPlatAlarmGetNow = NULL;
- kCallCountIndexMax
-};
-
-uint32_t sNow;
-uint32_t sPlatT0;
-uint32_t sPlatDt;
-bool sTimerOn;
-uint32_t sCallCount[kCallCountIndexMax];
+ g_testPlatRadioCaps = kRadioCapsNone;
+ g_testPlatRadioSetPanId = NULL;
+ g_testPlatRadioSetExtendedAddress = NULL;
+ g_testPlatRadioSetShortAddress = NULL;
+ g_testPlatRadioEnable = NULL;
+ g_testPlatRadioDisable = NULL;
+ g_testPlatRadioReceive = NULL;
+ g_testPlatRadioTransmit = NULL;
+ g_testPlatRadioGetTransmitBuffer = NULL;
+}
bool sDiagMode = false;
@@ -71,23 +87,43 @@ extern "C" {
// Alarm
//
- void otPlatAlarmStop(otInstance *)
+ void otPlatAlarmStop(otInstance *aInstance)
{
- sTimerOn = false;
- sCallCount[kCallCountIndexAlarmStop]++;
+ if (g_testPlatAlarmStop)
+ {
+ g_testPlatAlarmStop(aInstance);
+ }
+ else
+ {
+ g_testPlatAlarmSet = false;
+ }
}
- void otPlatAlarmStartAt(otInstance *, uint32_t aT0, uint32_t aDt)
+ void otPlatAlarmStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt)
{
- sTimerOn = true;
- sCallCount[kCallCountIndexAlarmStart]++;
- sPlatT0 = aT0;
- sPlatDt = aDt;
+ if (g_testPlatAlarmStartAt)
+ {
+ g_testPlatAlarmStartAt(aInstance, aT0, aDt);
+ }
+ else
+ {
+ g_testPlatAlarmSet = true;
+ g_testPlatAlarmNext = aT0 + aDt;
+ }
}
uint32_t otPlatAlarmGetNow(void)
{
- return sNow;
+ if (g_testPlatAlarmGetNow)
+ {
+ return g_testPlatAlarmGetNow();
+ }
+ else
+ {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (uint32_t)((tv.tv_sec * 1000) + (tv.tv_usec / 1000) + 123456);
+ }
}
//
@@ -98,30 +134,56 @@ extern "C" {
{
}
- void otPlatRadioSetPanId(otInstance *, uint16_t)
+ void otPlatRadioSetPanId(otInstance *aInstance, uint16_t aPanId)
{
+ if (g_testPlatRadioSetPanId)
+ {
+ g_testPlatRadioSetPanId(aInstance, aPanId);
+ }
}
- void otPlatRadioSetExtendedAddress(otInstance *, uint8_t *)
+ void otPlatRadioSetExtendedAddress(otInstance *aInstance, uint8_t *aExtAddr)
{
+ if (g_testPlatRadioSetExtendedAddress)
+ {
+ g_testPlatRadioSetExtendedAddress(aInstance, aExtAddr);
+ }
}
- void otPlatRadioSetShortAddress(otInstance *, uint16_t)
+ void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aShortAddr)
{
+ if (g_testPlatRadioSetShortAddress)
+ {
+ g_testPlatRadioSetShortAddress(aInstance, aShortAddr);
+ }
}
void otPlatRadioSetPromiscuous(otInstance *, bool)
{
}
- ThreadError otPlatRadioEnable(otInstance *)
+ ThreadError otPlatRadioEnable(otInstance *aInstance)
{
- return kThreadError_None;
+ if (g_testPlatRadioEnable)
+ {
+ return g_testPlatRadioEnable(aInstance);
+ }
+ else
+ {
+ return kThreadError_None;
+ }
}
- ThreadError otPlatRadioDisable(otInstance *)
+ ThreadError otPlatRadioDisable(otInstance *aInstance)
{
- return kThreadError_None;
+ if (g_testPlatRadioEnable)
+ {
+ return g_testPlatRadioDisable(aInstance);
+ }
+ else
+ {
+ return kThreadError_None;
+ }
}
ThreadError otPlatRadioSleep(otInstance *)
@@ -129,19 +191,40 @@ extern "C" {
return kThreadError_None;
}
- ThreadError otPlatRadioReceive(otInstance *, uint8_t)
+ ThreadError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel)
{
- return kThreadError_None;
+ if (g_testPlatRadioReceive)
+ {
+ return g_testPlatRadioReceive(aInstance, aChannel);
+ }
+ else
+ {
+ return kThreadError_None;
+ }
}
- ThreadError otPlatRadioTransmit(otInstance *)
+ ThreadError otPlatRadioTransmit(otInstance *aInstance)
{
- return kThreadError_None;
+ if (g_testPlatRadioTransmit)
+ {
+ return g_testPlatRadioTransmit(aInstance);
+ }
+ else
+ {
+ return kThreadError_None;
+ }
}
- RadioPacket *otPlatRadioGetTransmitBuffer(otInstance *)
+ RadioPacket *otPlatRadioGetTransmitBuffer(otInstance *aInstance)
{
- return (RadioPacket *)0;
+ if (g_testPlatRadioGetTransmitBuffer)
+ {
+ return g_testPlatRadioGetTransmitBuffer(aInstance);
+ }
+ else
+ {
+ return (RadioPacket *)0;
+ }
}
int8_t otPlatRadioGetRssi(otInstance *)
@@ -151,7 +234,7 @@ extern "C" {
otRadioCaps otPlatRadioGetCaps(otInstance *)
{
- return kRadioCapsNone;
+ return g_testPlatRadioCaps;
}
bool otPlatRadioGetPromiscuous(otInstance *)
diff --git a/tests/unit/test_platform.h b/tests/unit/test_platform.h
new file mode 100644
index 000000000..c6d7b8530
--- /dev/null
+++ b/tests/unit/test_platform.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2016, The OpenThread Authors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TEST_PLATFORM_H
+#define TEST_PLATFORM_H
+
+#if _WIN32
+#define _CRT_SECURE_NO_WARNINGS
+#include
+#include
+#endif
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "test_util.h"
+
+//
+// Alarm Platform
+//
+
+typedef void (*testPlatAlarmStop)(otInstance *);
+typedef void (*testPlatAlarmStartAt)(otInstance *, uint32_t, uint32_t);
+typedef uint32_t (*testPlatAlarmGetNow)(void);
+
+extern bool g_testPlatAlarmSet;
+extern uint32_t g_testPlatAlarmNext;
+extern testPlatAlarmStop g_testPlatAlarmStop;
+extern testPlatAlarmStartAt g_testPlatAlarmStartAt;
+extern testPlatAlarmGetNow g_testPlatAlarmGetNow;
+
+//
+// Radio Platform
+//
+
+typedef void (*testPlatRadioSetPanId)(otInstance *, uint16_t);
+typedef void (*testPlatRadioSetExtendedAddress)(otInstance *, uint8_t *);
+typedef void (*testPlatRadioSetShortAddress)(otInstance *, uint16_t);
+
+typedef ThreadError(*testPlatRadioEnable)(otInstance *);
+typedef ThreadError(*testPlatRadioDisable)(otInstance *);
+typedef ThreadError(*testPlatRadioReceive)(otInstance *, uint8_t);
+typedef ThreadError(*testPlatRadioTransmit)(otInstance *);
+typedef RadioPacket *(*testPlatRadioGetTransmitBuffer)(otInstance *);
+
+extern otRadioCaps g_testPlatRadioCaps;
+extern testPlatRadioSetPanId g_testPlatRadioSetPanId;
+extern testPlatRadioSetExtendedAddress g_testPlatRadioSetExtendedAddress;
+extern testPlatRadioSetShortAddress g_testPlatRadioSetShortAddress;
+extern testPlatRadioEnable g_testPlatRadioEnable;
+extern testPlatRadioDisable g_testPlatRadioDisable;
+extern testPlatRadioReceive g_testPlatRadioReceive;
+extern testPlatRadioTransmit g_testPlatRadioTransmit;
+extern testPlatRadioGetTransmitBuffer g_testPlatRadioGetTransmitBuffer;
+
+// Resets platform functions to defaults
+void testPlatResetToDefaults(void);
+
+#endif // TEST_PLATFORM_H
diff --git a/tests/unit/test_timer.cpp b/tests/unit/test_timer.cpp
index 232522a53..efb5a1b4b 100644
--- a/tests/unit/test_timer.cpp
+++ b/tests/unit/test_timer.cpp
@@ -26,12 +26,9 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-#include "test_util.h"
-#include
+#include "test_platform.h"
#include
#include
-#include
-#include
#include
enum
@@ -43,11 +40,37 @@ enum
kCallCountIndexMax
};
-extern uint32_t sNow;
-extern uint32_t sPlatT0;
-extern uint32_t sPlatDt;
-extern bool sTimerOn;
-extern uint32_t sCallCount[kCallCountIndexMax];
+uint32_t sNow;
+uint32_t sPlatT0;
+uint32_t sPlatDt;
+bool sTimerOn;
+uint32_t sCallCount[kCallCountIndexMax];
+
+void testTimerAlarmStop(otInstance *)
+{
+ sTimerOn = false;
+ sCallCount[kCallCountIndexAlarmStop]++;
+}
+
+void testTimerAlarmStartAt(otInstance *, uint32_t aT0, uint32_t aDt)
+{
+ sTimerOn = true;
+ sCallCount[kCallCountIndexAlarmStart]++;
+ sPlatT0 = aT0;
+ sPlatDt = aDt;
+}
+
+uint32_t testTimerAlarmGetNow(void)
+{
+ return sNow;
+}
+
+void InitTestTimer(void)
+{
+ g_testPlatAlarmStop = testTimerAlarmStop;
+ g_testPlatAlarmStartAt = testTimerAlarmStartAt;
+ g_testPlatAlarmGetNow = testTimerAlarmGetNow;
+}
void InitCounters(void)
{
@@ -76,6 +99,7 @@ int TestOneTimer(void)
// Test one Timer basic operation.
+ InitTestTimer();
InitCounters();
sNow = kTimeT0;
@@ -321,6 +345,7 @@ int TestTenTimers(void)
// Start the Ten timers.
+ InitTestTimer();
InitCounters();
for (i = 0; i < kNumTimers ; i++)
diff --git a/tests/unit/test_util.h b/tests/unit/test_util.h
index 1e26d2aec..0142dc482 100644
--- a/tests/unit/test_util.h
+++ b/tests/unit/test_util.h
@@ -72,6 +72,8 @@ extern "C" {
//
#define CompileTimeAssert(COND, MSG)
+#define Log(aFormat, ...) printf(aFormat "\n", ## __VA_ARGS__)
+
#ifdef __cplusplus
}
#endif
@@ -81,12 +83,17 @@ extern "C" {
typedef void (*utAssertTrue)(bool condition, const wchar_t *message);
extern utAssertTrue s_AssertTrue;
+typedef void (*utLogMessage)(const char *format, ...);
+extern utLogMessage s_LogMessage;
+
#define SuccessOrQuit(ERR, MSG) s_AssertTrue((ERR) == kThreadError_None, L##MSG)
#define VerifyOrQuit(ERR, MSG) s_AssertTrue(ERR, L##MSG)
#define CompileTimeAssert(COND, MSG) static_assert(COND, MSG)
+#define Log(aFormat, ...) s_LogMessage(aFormat, ## __VA_ARGS__)
+
#endif
#endif
diff --git a/tests/unit/test_windows.cpp b/tests/unit/test_windows.cpp
index 14eb3ffff..2e0c2826a 100644
--- a/tests/unit/test_windows.cpp
+++ b/tests/unit/test_windows.cpp
@@ -29,6 +29,7 @@
#include
#include "CppUnitTest.h"
#include "test_util.h"
+#include "test_platform.h"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
@@ -80,17 +81,21 @@ void test_packed2();
void test_packed_union();
void test_packed_enum();
+// test_fuzz.cpp
+void TestFuzz(uint32_t aSeconds);
+
#pragma endregion
utAssertTrue s_AssertTrue;
+utLogMessage s_LogMessage;
namespace Thread
-{
- TEST_CLASS(UnitTests)
- {
- public:
+{
+ TEST_CLASS(UnitTests)
+ {
+ public:
- UnitTests() { s_AssertTrue = AssertTrue; }
+ UnitTests() { s_AssertTrue = AssertTrue; s_LogMessage = LogMessage; }
// Helper for openthread test code to call
static void AssertTrue(bool condition, const wchar_t* message)
@@ -98,6 +103,25 @@ namespace Thread
Assert::IsTrue(condition, message);
}
+ // Helper for logging a message
+ static void LogMessage(const char* format, ...)
+ {
+ char message[512];
+
+ va_list args;
+ va_start(args, format);
+ vsnprintf(message, sizeof(message), format, args);
+ va_end(args);
+
+ Logger::WriteMessage(message);
+ }
+
+ // Make sure to reset the test platform functions before each test
+ TEST_METHOD_INITIALIZE(TestMethodInit)
+ {
+ testPlatResetToDefaults();
+ }
+
// test_aes.cpp
TEST_METHOD(TestMacBeaconFrame) { ::TestMacBeaconFrame(); }
TEST_METHOD(TestMacDataFrame) { ::TestMacDataFrame(); }
@@ -131,5 +155,8 @@ namespace Thread
TEST_METHOD(test_packed2) { ::test_packed2(); }
TEST_METHOD(test_packed_union) { ::test_packed_union(); }
TEST_METHOD(test_packed_enum) { ::test_packed_enum(); }
- };
+
+ // test_settings.cpp
+ TEST_METHOD(RunTestFuzz) { ::TestFuzz(0); }
+ };
}