static Iface_DEFUN sendMessage(struct Message* msg, struct Iface* iface) { struct ETHInterface_pvt* ctx = Identity_containerOf(iface, struct ETHInterface_pvt, pub.generic.iface); struct Sockaddr* sa = (struct Sockaddr*) msg->bytes; Assert_true(msg->length >= Sockaddr_OVERHEAD); Assert_true(sa->addrLen <= ETHInterface_Sockaddr_SIZE); struct ETHInterface_Sockaddr sockaddr = { .generic = { .addrLen = 0 } }; Message_pop(msg, &sockaddr, sa->addrLen, NULL); struct sockaddr_ll addr; Bits_memcpy(&addr, &ctx->addrBase, sizeof(struct sockaddr_ll)); if (sockaddr.generic.flags & Sockaddr_flags_BCAST) { Bits_memset(addr.sll_addr, 0xff, 6); } else { Bits_memcpy(addr.sll_addr, sockaddr.mac, 6); } struct ETHInterface_Header hdr = { .version = ETHInterface_CURRENT_VERSION, .zero = 0, .length_be = Endian_hostToBigEndian16(msg->length + ETHInterface_Header_SIZE), .fc00_be = Endian_hostToBigEndian16(0xfc00) }; Message_push(msg, &hdr, ETHInterface_Header_SIZE, NULL); struct Except* eh = NULL; sendMessageInternal(msg, &addr, ctx, eh); return NULL; } static void handleEvent2(struct ETHInterface_pvt* context, struct Allocator* messageAlloc) { struct Message* msg = Message_new(MAX_PACKET_SIZE, PADDING, messageAlloc); struct sockaddr_ll addr; uint32_t addrLen = sizeof(struct sockaddr_ll); // Knock it out of alignment by 2 bytes so that it will be // aligned when the idAndPadding is shifted off. Message_shift(msg, 2, NULL); int rc = recvfrom(context->socket, msg->bytes, msg->length, 0, (struct sockaddr*) &addr, &addrLen); if (rc < ETHInterface_Header_SIZE) { Log_debug(context->logger, "Failed to receive eth frame"); return; } Assert_true(msg->length >= rc); msg->length = rc; //Assert_true(addrLen == SOCKADDR_LL_LEN); struct ETHInterface_Header hdr; Message_pop(msg, &hdr, ETHInterface_Header_SIZE, NULL); // here we could put a switch statement to handle different versions differently. if (hdr.version != ETHInterface_CURRENT_VERSION) { Log_debug(context->logger, "DROP unknown version"); return; } uint16_t reportedLength = Endian_bigEndianToHost16(hdr.length_be); reportedLength -= ETHInterface_Header_SIZE; if (msg->length != reportedLength) { if (msg->length < reportedLength) { Log_debug(context->logger, "DROP size field is larger than frame"); return; } msg->length = reportedLength; } if (hdr.fc00_be != Endian_hostToBigEndian16(0xfc00)) { Log_debug(context->logger, "DROP bad magic"); return; } struct ETHInterface_Sockaddr sockaddr = { .zero = 0 }; Bits_memcpy(sockaddr.mac, addr.sll_addr, 6); sockaddr.generic.addrLen = ETHInterface_Sockaddr_SIZE; if (addr.sll_pkttype == PACKET_BROADCAST) { sockaddr.generic.flags |= Sockaddr_flags_BCAST; } Message_push(msg, &sockaddr, ETHInterface_Sockaddr_SIZE, NULL); Assert_true(!((uintptr_t)msg->bytes % 4) && "Alignment fault"); Iface_send(&context->pub.generic.iface, msg); } static void handleEvent(void* vcontext) { struct ETHInterface_pvt* context = Identity_check((struct ETHInterface_pvt*) vcontext); struct Allocator* messageAlloc = Allocator_child(context->pub.generic.alloc); handleEvent2(context, messageAlloc); Allocator_free(messageAlloc); } List* ETHInterface_listDevices(struct Allocator* alloc, struct Except* eh) { List* out = List_new(alloc); #ifndef android struct ifaddrs* ifaddr = NULL; if (getifaddrs(&ifaddr) || ifaddr == NULL) { Except_throw(eh, "getifaddrs() -> errno:%d [%s]", errno, strerror(errno)); } for (struct ifaddrs* ifa = ifaddr; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_PACKET) { List_addString(out, String_new(ifa->ifa_name, alloc), alloc); } } freeifaddrs(ifaddr); #endif return out; } static int closeSocket(struct Allocator_OnFreeJob* j) { struct ETHInterface_pvt* ctx = Identity_check((struct ETHInterface_pvt*) j->userData); close(ctx->socket); return 0; } struct ETHInterface* ETHInterface_new(struct EventBase* eventBase, const char* bindDevice, struct Allocator* alloc, struct Except* exHandler, struct Log* logger) { struct ETHInterface_pvt* ctx = Allocator_calloc(alloc, sizeof(struct ETHInterface_pvt), 1); Identity_set(ctx); ctx->pub.generic.iface.send = sendMessage; ctx->pub.generic.alloc = alloc; ctx->logger = logger; struct ifreq ifr = { .ifr_ifindex = 0 }; ctx->socket = socket(AF_PACKET, SOCK_DGRAM, Ethernet_TYPE_CJDNS); if (ctx->socket == -1) { Except_throw(exHandler, "call to socket() failed. [%s]", strerror(errno)); } Allocator_onFree(alloc, closeSocket, ctx); CString_strncpy(ifr.ifr_name, bindDevice, IFNAMSIZ - 1); ctx->ifName = String_new(bindDevice, alloc); if (ioctl(ctx->socket, SIOCGIFINDEX, &ifr) == -1) { Except_throw(exHandler, "failed to find interface index [%s]", strerror(errno)); } ctx->ifindex = ifr.ifr_ifindex; if (ioctl(ctx->socket, SIOCGIFFLAGS, &ifr) < 0) { Except_throw(exHandler, "ioctl(SIOCGIFFLAGS) [%s]", strerror(errno)); } if (!((ifr.ifr_flags & IFF_UP) && (ifr.ifr_flags & IFF_RUNNING))) { Log_info(logger, "Bringing up interface [%s]", ifr.ifr_name); ifr.ifr_flags |= IFF_UP | IFF_RUNNING; if (ioctl(ctx->socket, SIOCSIFFLAGS, &ifr) < 0) { Except_throw(exHandler, "ioctl(SIOCSIFFLAGS) [%s]", strerror(errno)); } } ctx->addrBase = (struct sockaddr_ll) { .sll_family = AF_PACKET, .sll_protocol = Ethernet_TYPE_CJDNS, .sll_ifindex = ctx->ifindex, .sll_hatype = ARPHRD_ETHER, .sll_pkttype = PACKET_OTHERHOST, .sll_halen = ETH_ALEN }; if (bind(ctx->socket, (struct sockaddr*) &ctx->addrBase, sizeof(struct sockaddr_ll))) { Except_throw(exHandler, "call to bind() failed [%s]", strerror(errno)); } Socket_makeNonBlocking(ctx->socket); Event_socketRead(handleEvent, ctx, ctx->socket, eventBase, alloc, exHandler); return &ctx->pub; }
TEST_F(TestRegionNoArgs,BasicRegion_ShallowHistory_Resume2) { typedef TestConcurrentCompositeStateNoArgsMock < TestStateMachineNoEventArgsMock > ConcurrentCompositeStateMockType; typedef TestRegionNoArgsMock < ConcurrentCompositeStateMockType , sttcl::CompositeStateHistoryType::Shallow > RegionMockType; typedef TestRegionInnerStateNoArgsMock<ConcurrentCompositeStateMockType,RegionMockType> RegionInnerStateMockType; ::testing::NiceMock<TestStateMachineNoEventArgsMock> stateMachine("stateMachine"); ::testing::NiceMock < ConcurrentCompositeStateMockType > compositeState(&stateMachine); ::testing::NiceMock < RegionMockType > region(&compositeState,"region"); ::testing::NiceMock < RegionInnerStateMockType > innerState1("innerState1"); ::testing::NiceMock < RegionInnerStateMockType > innerState2("innerState1"); ::testing::NiceMock < TestSimpleStateNoArgsMock<TestStateMachineNoEventArgsMock> > outerState("outerState"); stateMachine.autoFinalize(false); compositeState.setRegion(0,®ion); region.initialState(&innerState1); stateMachine.initialState(&compositeState); // Setup mock call expectations //---------------------------------------------------------------------------- EXPECT_CALL(compositeState,handleEvent1(&stateMachine)) .Times(1); EXPECT_CALL(compositeState,handleEvent2(&stateMachine)) .Times(1) .WillOnce( TRIGGER_STATE_CHANGE(ConcurrentCompositeStateMockType, handleEvent2, &compositeState, &outerState) ); EXPECT_CALL(compositeState,endDoImpl(&stateMachine)) .Times(2); EXPECT_CALL(compositeState,exitImpl(&stateMachine)) .Times(2); // Check region calls EXPECT_CALL(region,enterRegionImpl(&compositeState)) .Times(2); EXPECT_CALL(region,startingRegionThread()) .Times(2); EXPECT_CALL(region,initializeImpl(_)) .Times(2); // TODO: Eliminiate (superflous?) calls to finalize(Impl) EXPECT_CALL(region,finalizeImpl(_)) .Times(3); EXPECT_CALL(region,endingRegionThread()) .Times(2); EXPECT_CALL(region,exitRegionImpl(&compositeState)) .Times(2); EXPECT_CALL(outerState,entryImpl(&stateMachine)) .Times(1); EXPECT_CALL(outerState,startDoImpl(&stateMachine)) .Times(1); EXPECT_CALL(outerState,handleEvent3(&stateMachine)) .Times(1) .WillOnce( TRIGGER_STATE_CHANGE(TestSimpleStateNoArgsMock<TestStateMachineNoEventArgsMock>, handleEvent3, &outerState, &compositeState) ); EXPECT_CALL(outerState,endDoImpl(&stateMachine)) .Times(1); EXPECT_CALL(outerState,exitImpl(&stateMachine)) .Times(1); EXPECT_CALL(innerState1,entryImpl(®ion)) .Times(1); EXPECT_CALL(innerState1,startDoImpl(®ion)) .Times(1); // TODO: Eliminiate (superflous?) calls to initSubStateMachinesImpl EXPECT_CALL(innerState1,initSubStateMachinesImpl(true)) .Times(2); // EXPECT_CALL(innerState1,initSubStateMachinesImpl(true)) // .Times(1); EXPECT_CALL(innerState1,handleEvent1(&compositeState,®ion)) .Times(1) .WillOnce( TRIGGER_STATE_CHANGE2(RegionInnerStateMockType, handleEvent1, &innerState1, &innerState2) ); EXPECT_CALL(innerState1,endDoImpl(®ion)) .Times(1); EXPECT_CALL(innerState1,exitImpl(®ion)) .Times(1); EXPECT_CALL(innerState2,entryImpl(®ion)) .Times(1); EXPECT_CALL(innerState2,startDoImpl(®ion)) .Times(1); // EXPECT_CALL(innerState2,initSubStateMachinesImpl(true)) // .Times(1); EXPECT_CALL(innerState2,endDoImpl(®ion)) .Times(2); EXPECT_CALL(innerState2,exitImpl(®ion)) .Times(2); // Run the state machine //---------------------------------------------------------------------------- // STTCL_TEST_LOG_ALL(); stateMachine.initialize(true); // Give the region thread(s) a chance to run sttcl::internal::SttclThread<>::sleep(sttcl::TimeDuration<>(0,0,0,100)); stateMachine.triggerEvent1(); // Give the region thread(s) a chance to run sttcl::internal::SttclThread<>::sleep(sttcl::TimeDuration<>(0,0,0,100)); stateMachine.triggerEvent2(); // Give the region thread(s) a chance to run sttcl::internal::SttclThread<>::sleep(sttcl::TimeDuration<>(0,0,0,100)); stateMachine.triggerEvent3(); // Give the region thread(s) a chance to run sttcl::internal::SttclThread<>::sleep(sttcl::TimeDuration<>(0,0,0,100)); stateMachine.finalize(true); // STTCL_TEST_LOG_END(); }
TEST_F(TestConcurrentCompositeStateNoArgs,MultipleRegions) { typedef TestConcurrentCompositeStateNoArgsMock < TestStateMachineNoEventArgsMock > ConcurrentCompositeStateMockType; ::testing::NiceMock<TestStateMachineNoEventArgsMock> stateMachine; ::testing::NiceMock < ConcurrentCompositeStateMockType > compositeState(&stateMachine); ::testing::NiceMock < TestRegionNoArgsMock<ConcurrentCompositeStateMockType> > region1(&compositeState); ::testing::NiceMock < TestRegionNoArgsMock<ConcurrentCompositeStateMockType> > region2(&compositeState); stateMachine.autoFinalize(false); compositeState.setRegion(0,®ion1); compositeState.setRegion(1,®ion2); stateMachine.initialState(&compositeState); // Setup mock call expectations //---------------------------------------------------------------------------- EXPECT_CALL(compositeState,entryImpl(&stateMachine)) .Times(1); EXPECT_CALL(compositeState,startDoImpl(&stateMachine)) .Times(1); EXPECT_CALL(compositeState,endDoImpl(&stateMachine)) .Times(1); EXPECT_CALL(compositeState,exitImpl(&stateMachine)) .Times(1); // Check region calls EXPECT_CALL(region1,enterRegionImpl(&compositeState)) .Times(1); EXPECT_CALL(region1,startingRegionThread()) .Times(1); EXPECT_CALL(region1,initializeImpl(_)) .Times(1); EXPECT_CALL(region1,handleEvent1(&compositeState,®ion1)) .Times(1); EXPECT_CALL(region1,handleEvent2(&compositeState,®ion1)) .Times(1); EXPECT_CALL(region1,handleEvent3(&compositeState,®ion1)) .Times(1); EXPECT_CALL(region1,handleEvent4(&compositeState,®ion1)) .Times(1); EXPECT_CALL(region1,finalizeImpl(_)) .Times(1); EXPECT_CALL(region1,endingRegionThread()) .Times(1); EXPECT_CALL(region1,exitRegionImpl(&compositeState)) .Times(1); EXPECT_CALL(region2,enterRegionImpl(&compositeState)) .Times(1); EXPECT_CALL(region2,startingRegionThread()) .Times(1); EXPECT_CALL(region2,initializeImpl(_)) .Times(1); EXPECT_CALL(region2,handleEvent1(&compositeState,®ion2)) .Times(1); EXPECT_CALL(region2,handleEvent2(&compositeState,®ion2)) .Times(1); EXPECT_CALL(region2,handleEvent3(&compositeState,®ion2)) .Times(1); EXPECT_CALL(region2,handleEvent4(&compositeState,®ion2)) .Times(1); EXPECT_CALL(region2,finalizeImpl(_)) .Times(1); EXPECT_CALL(region2,endingRegionThread()) .Times(1); EXPECT_CALL(region2,exitRegionImpl(&compositeState)) .Times(1); // Run the state machine //---------------------------------------------------------------------------- // STTCL_TEST_LOG_ALL(); // compositeState.enableLogging(true); // innerState.enableLogging(true); stateMachine.initialize(); // Give the region thread(s) a chance to run sttcl::internal::SttclThread<>::sleep(sttcl::TimeDuration<>(0,0,0,100)); stateMachine.triggerEvent1(); // Give the region thread(s) a chance to run sttcl::internal::SttclThread<>::sleep(sttcl::TimeDuration<>(0,0,0,100)); stateMachine.triggerEvent2(); // Give the region thread(s) a chance to run sttcl::internal::SttclThread<>::sleep(sttcl::TimeDuration<>(0,0,0,100)); stateMachine.triggerEvent3(); // Give the region thread(s) a chance to run sttcl::internal::SttclThread<>::sleep(sttcl::TimeDuration<>(0,0,0,100)); stateMachine.triggerEvent4(); // Give the region thread(s) a chance to run sttcl::internal::SttclThread<>::sleep(sttcl::TimeDuration<>(0,0,0,100)); stateMachine.finalize(); // Give the region thread(s) a chance to run sttcl::internal::SttclThread<>::sleep(sttcl::TimeDuration<>(0,0,0,100)); EXPECT_TRUE(region1.waitForDoActionExited(sttcl::TimeDuration<>(0,0,0,100),10)); // STTCL_TEST_LOG_END(); }