// IACore-OSS; The Core Library for All IA Open Source Projects // Copyright (C) 2026 IAS (ias@iasoft.dev) // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include using namespace IACore; IAT_BEGIN_BLOCK(Core, IPC) // ------------------------------------------------------------------------- // 1. Layout Constraints // ------------------------------------------------------------------------- auto test_layout_constraints() -> bool { // Verify alignment IAT_CHECK_EQ(alignof(IpcSharedMemoryLayout), static_cast(64)); // Verify offsets to ensure cache-line isolation // Header is at 0 IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, meta), static_cast(0)); // moni_control should be at 64 (cache line 1) IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, moni_control), static_cast(64)); // ControlBlock is 128 bytes (64 for producer, 64 for consumer) // So mino_control should be at 64 + 128 = 192 IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, mino_control), static_cast(192)); // Data offsets should follow mino_control (192 + 128 = 320) IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, moni_data_offset), static_cast(320)); // Ensure the whole struct is a multiple of 64 IAT_CHECK_EQ(sizeof(IpcSharedMemoryLayout) % 64, static_cast(0)); return true; } // ------------------------------------------------------------------------- // 2. Manual Shared Memory & RingBuffer Setup // ------------------------------------------------------------------------- auto test_manual_shm_ringbuffer() -> bool { const String shm_name = "IA_TEST_IPC_LAYOUT_CHECK"; const usize shm_size = 16 * 1024; // 16KB // 1. Create Shared Memory // Ensure it doesn't exist first FileOps::unlink_shared_memory(shm_name); auto map_res = FileOps::map_shared_memory(shm_name, shm_size, true); IAT_CHECK(map_res.has_value()); u8 *base_ptr = *map_res; auto *layout = reinterpret_cast(base_ptr); // 2. Initialize Layout // We simulate what IpcManager would do layout->meta.magic = 0xDEADBEEF; // Dummy layout->meta.version = 1; layout->meta.total_size = shm_size; // Define data regions // Data starts after the layout struct const usize header_size = IpcSharedMemoryLayout::get_header_size(); const usize data_available = shm_size - header_size; const usize half_data = data_available / 2; layout->moni_data_offset = header_size; layout->moni_data_size = half_data; layout->mino_data_offset = header_size + half_data; layout->mino_data_size = half_data; // 3. Initialize RingBuffers // MONI (Manager Out, Node In) // Manager is Owner of MONI Span moni_data_span(base_ptr + layout->moni_data_offset, static_cast(layout->moni_data_size)); auto moni_res = RingBufferView::create(&layout->moni_control, moni_data_span, true); IAT_CHECK(moni_res.has_value()); auto moni = std::move(*moni_res); // MINO (Manager In, Node Out) // Manager is NOT Owner of MINO (Node writes to it) // But for this test, let's pretend we are the Node for MINO to test writing Span mino_data_span(base_ptr + layout->mino_data_offset, static_cast(layout->mino_data_size)); auto mino_res = RingBufferView::create(&layout->mino_control, mino_data_span, true); IAT_CHECK(mino_res.has_value()); auto _ = std::move(*mino_res); // 4. Test Data Flow // Write to MONI String msg = "IPC_TEST_MESSAGE"; IAT_CHECK( moni.push(100, Span(reinterpret_cast(msg.data()), msg.size())) .has_value()); // Read from MONI (Simulate Node reading) // Create a reader view auto moni_reader_res = RingBufferView::create(&layout->moni_control, moni_data_span, false); IAT_CHECK(moni_reader_res.has_value()); auto moni_reader = std::move(*moni_reader_res); RingBufferView::PacketHeader header; u8 buffer[128]; auto pop_res = moni_reader.pop(header, Span(buffer, 128)); IAT_CHECK(pop_res.has_value()); IAT_CHECK(pop_res->has_value()); // Should have data IAT_CHECK_EQ(header.id, static_cast(100)); String received((char *)buffer, *pop_res.value()); IAT_CHECK_EQ(received, msg); // Cleanup FileOps::unmap_file(base_ptr); FileOps::unlink_shared_memory(shm_name); return true; } // ------------------------------------------------------------------------- // 3. Manager Instantiation // ------------------------------------------------------------------------- class TestManager : public IpcManager { public: void on_signal(NativeProcessID, u8) override {} void on_packet(NativeProcessID, u16, Span) override {} }; auto test_manager_instantiation() -> bool { TestManager mgr; return true; } IAT_BEGIN_TEST_LIST() IAT_ADD_TEST(test_layout_constraints); IAT_ADD_TEST(test_manual_shm_ringbuffer); IAT_ADD_TEST(test_manager_instantiation); IAT_END_TEST_LIST() IAT_END_BLOCK() IAT_REGISTER_ENTRY(Core, IPC)