Merge pull request #58 from SamTebbs33/feature/arch-mock-testing

Feature/arch mock testing
This commit is contained in:
Edward Dean 2019-09-08 20:52:41 +01:00 committed by GitHub
commit b046cd8f3e
24 changed files with 1812 additions and 829 deletions

View file

@ -7,48 +7,59 @@ Pluto is a kernel written almost entirely in [Zig](https://github.com/ziglang/zi
![Hello image](hello.jpg) ![Hello image](hello.jpg)
## Goals ## Goals
* **Should be written in Zig as much as possible**. Assembly should only be used where required for functionality or performance reasons. * **Should be written in Zig as much as possible**. Assembly should only be used where required for functionality or performance reasons.
* **Light and performant**. The kernel should be usable both on embedded and desktop class CPUs, made possible by it being lightweight and modular. * **Light and performant**. The kernel should be usable both on embedded and desktop class CPUs, made possible by it being lightweight and modular.
* **Basic utilities will be written in Zig**. This includes a basic text editor and shell, and will be part of the filsystem external to the kernel itself. * **Basic utilities will be written in Zig**. This includes a basic text editor and shell, and will be part of the filesystem external to the kernel itself.
* **Easy to port**. The kernel is oblivous to the underlying architecture, meaning that ports only need to implement the defined interface and they should work without a hitch. * **Easy to port**. The kernel is oblivious to the underlying architecture, meaning that ports only need to implement the defined interface and they should work without a hitch.
All of these goals will benefit from the features of Zig. All of these goals will benefit from the features of Zig.
## Build ## Build
Requires a master build of Zig ([downloaded](https://ziglang.org/download) or [built from source](https://github.com/ziglang/zig#building-from-source)) *xorriso* and the grub tools (such as *grub-mkrescue*). A gdb binary compatible with your chosen target is required to run the kernel (e.g. *qemu-system-i386*). Requires a master build of Zig ([downloaded](https://ziglang.org/download) or [built from source](https://github.com/ziglang/zig#building-from-source)) *xorriso* and the grub tools (such as *grub-mkrescue*). A gdb binary compatible with your chosen target is required to run the kernel (e.g. *qemu-system-i386*).
```
```Shell
zig build zig build
``` ```
## Run ## Run
```
```Shell
zig build run zig build run
``` ```
## Debug ## Debug
Launch a gdb instance and connect to qemu. Launch a gdb instance and connect to qemu.
```
```Shell
zig build debug zig build debug
``` ```
## Test ## Test
Run the unitests or runtime tests.
``` Run the unit tests or runtime tests.
```Shell
zig build test zig build test
``` ```
## Options ## Options
* `-Ddebug=`: Boolean (default `false`). * `-Ddebug=`: Boolean (default `false`).
* **build**: Build with debug info included or stripped (see #70 for planned changes). * **build**: Build with debug info included or stripped (see #70 for planned changes).
* **run**: Wait for a gdb connection before executing. * **run**: Wait for a gdb connection before executing.
* `-Drt-test=`: Boolean (default `false`). * `-Drt-test=`: Boolean (default `false`).
* **build**: Build with runtime testing enabled. Makes the kernel bigger and slower but tests important functionality. * **build**: Build with runtime testing enabled. Makes the kernel bigger and slower but tests important functionality.
* **test**: Run the runtime testing script instead of the unittests. Checks for the expected log statements and fails if any are missing. * **test**: Run the runtime testing script instead of the unittests. Checks for the expected log statements and fails if any are missing.
## Contribution ## Contribution
We welcome all contributions, be it bug reports, feature suggestions or pull requests. We follow the style mandated by zig fmt so make sure you've run `zig fmt` on your code before submitting it. We welcome all contributions, be it bug reports, feature suggestions or pull requests. We follow the style mandated by zig fmt so make sure you've run `zig fmt` on your code before submitting it.
We also like to order a file's members (public after non-public): We also like to order a file's members (public after non-public):
1. imports 1. imports
2. type definitions 2. type definitions
3. constants 3. constants

View file

@ -27,9 +27,8 @@ steps:
- script: zig*/zig build - script: zig*/zig build
displayName: 'Build kernel' displayName: 'Build kernel'
# Uncomment once mock testing is finished - script: zig*/zig build test
#- script: zig*/zig build test displayName: 'Unit tests'
# displayName: 'Mocked tests'
- script: | - script: |
sudo apt-get update sudo apt-get update

View file

@ -4,6 +4,7 @@ const Builder = std.build.Builder;
const Step = std.build.Step; const Step = std.build.Step;
const Target = std.build.Target; const Target = std.build.Target;
const fs = std.fs; const fs = std.fs;
const Mode = builtin.Mode;
pub fn build(b: *Builder) !void { pub fn build(b: *Builder) !void {
const target = Target{ const target = Target{
@ -13,12 +14,19 @@ pub fn build(b: *Builder) !void {
.abi = .gnu, .abi = .gnu,
}, },
}; };
const target_str = switch (target.getArch()) {
builtin.Arch.i386 => "x86",
else => unreachable,
};
const debug = b.option(bool, "debug", "build with debug symbols / make qemu wait for a debug connection") orelse false; const debug = b.option(bool, "debug", "build with debug symbols / make qemu wait for a debug connection") orelse false;
const rt_test = b.option(bool, "rt-test", "enable/disable runtime testing") orelse false; const rt_test = b.option(bool, "rt-test", "enable/disable runtime testing") orelse false;
const main_src = "src/kernel/kmain.zig"; const main_src = "src/kernel/kmain.zig";
const exec = b.addExecutable("pluto", main_src); const exec = b.addExecutable("pluto", main_src);
exec.setMainPkgPath("."); exec.setMainPkgPath(".");
const const_path = try fs.path.join(b.allocator, [_][]const u8{ "src/kernel/arch/", target_str, "/constants.zig" });
exec.addPackagePath("constants", const_path);
exec.addBuildOption(bool, "rt_test", rt_test); exec.addBuildOption(bool, "rt_test", rt_test);
exec.setLinkerScriptPath("link.ld"); exec.setLinkerScriptPath("link.ld");
exec.setTheTarget(target); exec.setTheTarget(target);
@ -72,10 +80,17 @@ pub fn build(b: *Builder) !void {
const script = b.addSystemCommand([_][]const u8{ "python3", "test/rt-test.py", "x86", b.zig_exe }); const script = b.addSystemCommand([_][]const u8{ "python3", "test/rt-test.py", "x86", b.zig_exe });
test_step.dependOn(&script.step); test_step.dependOn(&script.step);
} else { } else {
const unit_tests = b.addTest(main_src); inline for ([_]Mode{ Mode.Debug, Mode.ReleaseFast, Mode.ReleaseSafe, Mode.ReleaseSmall }) |test_mode| {
unit_tests.setMainPkgPath("."); const mode_str = comptime modeToString(test_mode);
unit_tests.addBuildOption(bool, "rt_test", rt_test); const unit_tests = b.addTest("test/unittests/test_all.zig");
test_step.dependOn(&unit_tests.step); unit_tests.setBuildMode(test_mode);
unit_tests.setMainPkgPath(".");
unit_tests.setNamePrefix(mode_str ++ " - ");
unit_tests.addPackagePath("mocking", "test/mock/kernel/mocking.zig");
unit_tests.addPackagePath("constants", const_path);
unit_tests.addBuildOption(bool, "rt_test", rt_test);
test_step.dependOn(&unit_tests.step);
}
} }
const debug_step = b.step("debug", "Debug with gdb"); const debug_step = b.step("debug", "Debug with gdb");
@ -91,3 +106,12 @@ pub fn build(b: *Builder) !void {
}); });
debug_step.dependOn(&debug_cmd.step); debug_step.dependOn(&debug_cmd.step);
} }
fn modeToString(comptime mode: Mode) []const u8 {
return switch (mode) {
Mode.Debug => "debug",
Mode.ReleaseFast => "release-fast",
Mode.ReleaseSafe => "release-safe",
Mode.ReleaseSmall => "release-small",
};
}

View file

@ -1,6 +1,6 @@
const builtin = @import("builtin"); const builtin = @import("builtin");
pub const internals = switch (builtin.arch) { pub const internals = if (builtin.is_test) @import("mocking").arch else switch (builtin.arch) {
builtin.Arch.i386 => @import("arch/x86/arch.zig"), builtin.Arch.i386 => @import("arch/x86/arch.zig"),
else => unreachable, else => unreachable,
}; };

View file

@ -1,5 +1,6 @@
// Zig version: 0.4.0 // Zig version: 0.4.0
const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
const gdt = @import("gdt.zig"); const gdt = @import("gdt.zig");
const idt = @import("idt.zig"); const idt = @import("idt.zig");
@ -7,6 +8,8 @@ const irq = @import("irq.zig");
const isr = @import("isr.zig"); const isr = @import("isr.zig");
const log = @import("../../log.zig"); const log = @import("../../log.zig");
const pit = @import("pit.zig"); const pit = @import("pit.zig");
const paging = @import("paging.zig");
const MemProfile = @import("../../mem.zig").MemProfile;
const syscalls = @import("syscalls.zig"); const syscalls = @import("syscalls.zig");
pub const InterruptContext = struct { pub const InterruptContext = struct {
@ -39,9 +42,6 @@ pub const InterruptContext = struct {
user_esp: u32, user_esp: u32,
ss: u32, ss: u32,
}; };
const paging = @import("paging.zig");
const std = @import("std");
const MemProfile = @import("../../mem.zig").MemProfile;
/// ///
/// Initialise the architecture /// Initialise the architecture

View file

@ -1,4 +1,4 @@
const constants = @import("constants.zig"); const constants = @import("constants");
const ALIGN = 1 << 0; const ALIGN = 1 << 0;
const MEMINFO = 1 << 1; const MEMINFO = 1 << 1;

View file

@ -7,7 +7,8 @@ const MemProfile = @import("../../mem.zig").MemProfile;
const testing = @import("std").testing; const testing = @import("std").testing;
const expectEqual = testing.expectEqual; const expectEqual = testing.expectEqual;
const expect = testing.expect; const expect = testing.expect;
const constants = @import("constants.zig");
extern var KERNEL_ADDR_OFFSET: *u32;
const ENTRIES_PER_DIRECTORY = 1024; const ENTRIES_PER_DIRECTORY = 1024;
@ -34,7 +35,7 @@ const ENTRY_AVAILABLE = 0xE00;
const ENTRY_PAGE_ADDR = 0xFFC00000; const ENTRY_PAGE_ADDR = 0xFFC00000;
const Directory = packed struct { const Directory = packed struct {
entries: [ENTRIES_PER_DIRECTORY]DirectoryEntry entries: [ENTRIES_PER_DIRECTORY]DirectoryEntry,
}; };
const PagingError = error { const PagingError = error {
@ -42,7 +43,7 @@ const PagingError = error {
InvalidVirtAddresses, InvalidVirtAddresses,
PhysicalVirtualMismatch, PhysicalVirtualMismatch,
UnalignedPhysAddresses, UnalignedPhysAddresses,
UnalignedVirtAddresses UnalignedVirtAddresses,
}; };
/// ///
@ -140,7 +141,7 @@ pub fn init(mem_profile: *const MemProfile, allocator: *std.mem.Allocator) void
@memset(@ptrCast([*]u8, kernel_directory), 0, @sizeOf(Directory)); @memset(@ptrCast([*]u8, kernel_directory), 0, @sizeOf(Directory));
mapDir(kernel_directory, p_start, p_end, v_start, v_end, allocator) catch unreachable; mapDir(kernel_directory, p_start, p_end, v_start, v_end, allocator) catch unreachable;
const dir_physaddr = @ptrToInt(kernel_directory) - constants.KERNEL_ADDR_OFFSET; const dir_physaddr = @ptrToInt(kernel_directory) - @ptrToInt(&KERNEL_ADDR_OFFSET);
asm volatile ("mov %[addr], %%cr3" :: [addr] "{eax}" (dir_physaddr)); asm volatile ("mov %[addr], %%cr3" :: [addr] "{eax}" (dir_physaddr));
isr.registerIsr(14, pageFault) catch unreachable; isr.registerIsr(14, pageFault) catch unreachable;
} }

View file

@ -2,13 +2,13 @@
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
const arch = if (builtin.is_test) @import("../../test/kernel/arch_mock.zig") else @import("arch.zig").internals; const arch = @import("arch.zig").internals;
const multiboot = @import("multiboot.zig"); const multiboot = @import("multiboot.zig");
const tty = @import("tty.zig"); const tty = @import("tty.zig");
const vga = @import("vga.zig"); const vga = @import("vga.zig");
const log = @import("log.zig"); const log = @import("log.zig");
const serial = @import("serial.zig"); const serial = @import("serial.zig");
const mem = @import("mem.zig"); const mem = if (builtin.is_test) @import("mocking").mem else @import("mem.zig");
const options = @import("build_options"); const options = @import("build_options");
comptime { comptime {
@ -18,9 +18,13 @@ comptime {
} }
} }
// This is for unit testing as we need to export KERNEL_ADDR_OFFSET as it is no longer available
// from the linker script
export var KERNEL_ADDR_OFFSET: u32 = if (builtin.is_test) 0xC0000000 else undefined;
// Need to import this as we need the panic to be in the root source file, or zig will just use the // Need to import this as we need the panic to be in the root source file, or zig will just use the
// builtin panic and just loop, which is what we don't want // builtin panic and just loop, which is what we don't want
const panic_root = @import("panic.zig"); const panic_root = if (builtin.is_test) @import("mocking").panic else @import("panic.zig");
// Just call the panic function, as this need to be in the root source file // Just call the panic function, as this need to be in the root source file
pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn {

View file

@ -5,7 +5,7 @@ pub const Level = enum {
INFO, INFO,
DEBUG, DEBUG,
WARNING, WARNING,
ERROR ERROR,
}; };
fn logCallback(context: void, str: []const u8) anyerror!void { fn logCallback(context: void, str: []const u8) anyerror!void {
@ -23,9 +23,11 @@ pub fn logInfo(comptime format: []const u8, args: ...) void {
pub fn logDebug(comptime format: []const u8, args: ...) void { pub fn logDebug(comptime format: []const u8, args: ...) void {
log(Level.DEBUG, format, args); log(Level.DEBUG, format, args);
} }
pub fn logWarning(comptime format: []const u8, args: ...) void { pub fn logWarning(comptime format: []const u8, args: ...) void {
log(Level.WARNING, format, args); log(Level.WARNING, format, args);
} }
pub fn logError(comptime format: []const u8, args: ...) void { pub fn logError(comptime format: []const u8, args: ...) void {
log(Level.ERROR, format, args); log(Level.ERROR, format, args);
} }

View file

@ -6,7 +6,7 @@ pub const MemProfile = struct {
physaddr_end: [*]u8, physaddr_end: [*]u8,
physaddr_start: [*]u8, physaddr_start: [*]u8,
mem_kb: u32, mem_kb: u32,
fixed_alloc_size: u32 fixed_alloc_size: u32,
}; };
// The virtual/physical start/end of the kernel code // The virtual/physical start/end of the kernel code

View file

@ -2,7 +2,7 @@
const builtin = @import("builtin"); const builtin = @import("builtin");
const tty = @import("tty.zig"); const tty = @import("tty.zig");
const arch = if (builtin.is_test) @import("../../test/kernel/arch_mock.zig") else @import("arch.zig").internals; const arch = @import("arch.zig").internals;
pub fn panicFmt(trace: ?*builtin.StackTrace, comptime format: []const u8, args: ...) noreturn { pub fn panicFmt(trace: ?*builtin.StackTrace, comptime format: []const u8, args: ...) noreturn {
@setCold(true); @setCold(true);

File diff suppressed because it is too large Load diff

View file

@ -1,70 +1,64 @@
// Zig version: 0.4.0
const builtin = @import("builtin");
const arch = @import("arch.zig").internals; const arch = @import("arch.zig").internals;
const expectEqual = @import("std").testing.expectEqual;
const warn = @import("std").debug.warn;
/// The port address for the VGA register selection. /// The port address for the VGA register selection.
const PORT_ADDRESS: u16 = 0x03D4; pub const PORT_ADDRESS: u16 = 0x03D4;
/// The port address for the VGA data. /// The port address for the VGA data.
const PORT_DATA: u16 = 0x03D5; pub const PORT_DATA: u16 = 0x03D5;
// The indexes that is passed to the address port to select the register for the data to be /// The indexes that is passed to the address port to select the register for the data to be
// read or written to. /// read or written to.
const REG_HORIZONTAL_TOTAL: u8 = 0x00; pub const REG_HORIZONTAL_TOTAL: u8 = 0x00;
const REG_HORIZONTAL_DISPLAY_ENABLE_END: u8 = 0x01; pub const REG_HORIZONTAL_DISPLAY_ENABLE_END: u8 = 0x01;
const REG_START_HORIZONTAL_BLINKING: u8 = 0x02; pub const REG_START_HORIZONTAL_BLINKING: u8 = 0x02;
const REG_END_HORIZONTAL_BLINKING: u8 = 0x03; pub const REG_END_HORIZONTAL_BLINKING: u8 = 0x03;
const REG_START_HORIZONTAL_RETRACE_PULSE: u8 = 0x04; pub const REG_START_HORIZONTAL_RETRACE_PULSE: u8 = 0x04;
const REG_END_HORIZONTAL_RETRACE_PULSE: u8 = 0x05; pub const REG_END_HORIZONTAL_RETRACE_PULSE: u8 = 0x05;
const REG_VERTICAL_TOTAL: u8 = 0x06; pub const REG_VERTICAL_TOTAL: u8 = 0x06;
const REG_OVERFLOW: u8 = 0x07; pub const REG_OVERFLOW: u8 = 0x07;
const REG_PRESET_ROW_SCAN: u8 = 0x08; pub const REG_PRESET_ROW_SCAN: u8 = 0x08;
const REG_MAXIMUM_SCAN_LINE: u8 = 0x09; pub const REG_MAXIMUM_SCAN_LINE: u8 = 0x09;
/// The command for setting the start of the cursor scan line. /// The register select for setting the cursor scan lines.
const REG_CURSOR_START: u8 = 0x0A; pub const REG_CURSOR_START: u8 = 0x0A;
pub const REG_CURSOR_END: u8 = 0x0B;
pub const REG_START_ADDRESS_HIGH: u8 = 0x0C;
pub const REG_START_ADDRESS_LOW: u8 = 0x0D;
/// The command for setting the end of the cursor scan line. /// The command for setting the cursor's linear location.
const REG_CURSOR_END: u8 = 0x0B; pub const REG_CURSOR_LOCATION_HIGH: u8 = 0x0E;
const REG_START_ADDRESS_HIGH: u8 = 0x0C; pub const REG_CURSOR_LOCATION_LOW: u8 = 0x0F;
const REG_START_ADDRESS_LOW: u8 = 0x0D;
/// The command for setting the upper byte of the cursor's linear location. /// Other VGA registers.
const REG_CURSOR_LOCATION_HIGH: u8 = 0x0E; pub const REG_VERTICAL_RETRACE_START: u8 = 0x10;
pub const REG_VERTICAL_RETRACE_END: u8 = 0x11;
pub const REG_VERTICAL_DISPLAY_ENABLE_END: u8 = 0x12;
pub const REG_OFFSET: u8 = 0x13;
pub const REG_UNDERLINE_LOCATION: u8 = 0x14;
pub const REG_START_VERTICAL_BLINKING: u8 = 0x15;
pub const REG_END_VERTICAL_BLINKING: u8 = 0x16;
pub const REG_CRT_MODE_CONTROL: u8 = 0x17;
pub const REG_LINE_COMPARE: u8 = 0x18;
/// The command for setting the lower byte of the cursor's linear location. /// The start of the cursor scan line, the very beginning.
const REG_CURSOR_LOCATION_LOW: u8 = 0x0F; pub const CURSOR_SCANLINE_START: u8 = 0x0;
const REG_VERTICAL_RETRACE_START: u8 = 0x10;
const REG_VERTICAL_RETRACE_END: u8 = 0x11;
const REG_VERTICAL_DISPLAY_ENABLE_END: u8 = 0x12;
const REG_OFFSET: u8 = 0x13;
const REG_UNDERLINE_LOCATION: u8 = 0x14;
const REG_START_VERTICAL_BLINKING: u8 = 0x15;
const REG_END_VERTICAL_BLINKING: u8 = 0x16;
const REG_CRT_MODE_CONTROL: u8 = 0x17;
const REG_LINE_COMPARE: u8 = 0x18;
/// The scan line for use in the underline cursor shape.
pub const CURSOR_SCANLINE_MIDDLE: u8 = 0xE;
///The start of the cursor scan line, the very beginning. /// The end of the cursor scan line, the very end.
const CURSOR_SCANLINE_START: u8 = 0x0; pub const CURSOR_SCANLINE_END: u8 = 0xF;
///The scan line for use in the underline cursor shape.
const CURSOR_SCANLINE_MIDDLE: u8 = 0xE;
///The end of the cursor scan line, the very end.
const CURSOR_SCANLINE_END: u8 = 0xF;
/// If set, disables the cursor. /// If set, disables the cursor.
const CURSOR_DISABLE: u8 = 0x20; pub const CURSOR_DISABLE: u8 = 0x20;
/// The number of characters wide the screen is.
pub const WIDTH: u16 = 80; pub const WIDTH: u16 = 80;
/// The number of characters heigh the screen is.
pub const HEIGHT: u16 = 25; pub const HEIGHT: u16 = 25;
// The set of colours that VGA supports and can display for the foreground and background. /// The set of colours that VGA supports and can display for the foreground and background.
pub const COLOUR_BLACK: u4 = 0x00; pub const COLOUR_BLACK: u4 = 0x00;
pub const COLOUR_BLUE: u4 = 0x01; pub const COLOUR_BLUE: u4 = 0x01;
pub const COLOUR_GREEN: u4 = 0x02; pub const COLOUR_GREEN: u4 = 0x02;
@ -83,7 +77,7 @@ pub const COLOUR_LIGHT_BROWN: u4 = 0x0E;
pub const COLOUR_WHITE: u4 = 0x0F; pub const COLOUR_WHITE: u4 = 0x0F;
/// The set of shapes that can be displayed. /// The set of shapes that can be displayed.
pub const CursorShape = enum(u1) { pub const CursorShape = enum {
/// The cursor has the underline shape. /// The cursor has the underline shape.
UNDERLINE, UNDERLINE,
@ -97,6 +91,28 @@ var cursor_scanline_start: u8 = undefined;
/// The cursor scan line end so to know whether is in block or underline mode. /// The cursor scan line end so to know whether is in block or underline mode.
var cursor_scanline_end: u8 = undefined; var cursor_scanline_end: u8 = undefined;
/// A inline function for setting the VGA register port to read from or write to.
inline fn sendPort(port: u8) void {
arch.outb(PORT_ADDRESS, port);
}
/// A inline function for sending data to the set VGA register port.
inline fn sendData(data: u8) void {
arch.outb(PORT_DATA, data);
}
/// A inline function for setting the VGA register port to read from or write toa and sending data
/// to the set VGA register port.
inline fn sendPortData(port: u8, data: u8) void {
sendPort(port);
sendData(data);
}
/// A inline function for getting data from a set VGA register port.
inline fn getData() u8 {
return arch.inb(PORT_DATA);
}
/// ///
/// Takes two 4 bit values that represent the foreground and background colour of the text and /// Takes two 4 bit values that represent the foreground and background colour of the text and
/// returns a 8 bit value that gives both to be displayed. /// returns a 8 bit value that gives both to be displayed.
@ -105,7 +121,7 @@ var cursor_scanline_end: u8 = undefined;
/// IN fg: u4 - The foreground colour. /// IN fg: u4 - The foreground colour.
/// IN bg: u4 - The background colour. /// IN bg: u4 - The background colour.
/// ///
/// Return: /// Return: u8
/// Both combined into 1 byte for the colour to be displayed. /// Both combined into 1 byte for the colour to be displayed.
/// ///
pub fn entryColour(fg: u4, bg: u4) u8 { pub fn entryColour(fg: u4, bg: u4) u8 {
@ -117,30 +133,25 @@ pub fn entryColour(fg: u4, bg: u4) u8 {
/// background colour. /// background colour.
/// ///
/// Arguments: /// Arguments:
/// IN uc: u8 - The character. /// IN char: u8 - The character ro display.
/// IN colour: u8 - The foreground and background colour. /// IN colour: u8 - The foreground and background colour.
/// ///
/// Return: /// Return: u16
/// The VGA entry. /// A VGA entry.
/// ///
pub fn entry(uc: u8, colour: u8) u16 { pub fn entry(char: u8, colour: u8) u16 {
return u16(uc) | u16(colour) << 8; return u16(char) | u16(colour) << 8;
} }
/// ///
/// Update the hardware on screen cursor. /// Update the hardware on screen cursor.
/// ///
/// Arguments: /// Arguments:
/// IN x: u16 - The horizontal position of the cursor. /// IN x: u16 - The horizontal position of the cursor (column).
/// IN y: u16 - The vertical position of the cursor. /// IN y: u16 - The vertical position of the cursor (row).
///
/// Return:
/// The VGA entry.
/// ///
pub fn updateCursor(x: u16, y: u16) void { pub fn updateCursor(x: u16, y: u16) void {
var pos: u16 = undefined; var pos: u16 = undefined;
var pos_upper: u16 = undefined;
var pos_lower: u16 = undefined;
// Make sure new cursor position is within the screen // Make sure new cursor position is within the screen
if (x < WIDTH and y < HEIGHT) { if (x < WIDTH and y < HEIGHT) {
@ -150,31 +161,28 @@ pub fn updateCursor(x: u16, y: u16) void {
pos = (HEIGHT - 1) * WIDTH + (WIDTH - 1); pos = (HEIGHT - 1) * WIDTH + (WIDTH - 1);
} }
pos_upper = (pos >> 8) & 0x00FF; const pos_upper = (pos >> 8) & 0x00FF;
pos_lower = pos & 0x00FF; const pos_lower = pos & 0x00FF;
// Set the cursor position // Set the cursor position
arch.outb(PORT_ADDRESS, REG_CURSOR_LOCATION_LOW); sendPortData(REG_CURSOR_LOCATION_LOW, @truncate(u8, pos_lower));
arch.outb(PORT_DATA, @truncate(u8, pos_lower)); sendPortData(REG_CURSOR_LOCATION_HIGH, @truncate(u8, pos_upper));
arch.outb(PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH);
arch.outb(PORT_DATA, @truncate(u8, pos_upper));
} }
/// ///
/// Get the hardware cursor position. /// Get the linear position of the hardware cursor.
/// ///
/// Return: /// Return: u16
/// The linear cursor position. /// The linear cursor position.
/// ///
pub fn getCursor() u16 { pub fn getCursor() u16 {
var cursor: u16 = 0; var cursor: u16 = 0;
arch.outb(PORT_ADDRESS, REG_CURSOR_LOCATION_LOW); sendPort(REG_CURSOR_LOCATION_LOW);
cursor |= u16(arch.inb(PORT_DATA)); cursor |= u16(getData());
arch.outb(PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH); sendPort(REG_CURSOR_LOCATION_HIGH);
cursor |= u16(arch.inb(PORT_DATA)) << 8; cursor |= u16(getData()) << 8;
return cursor; return cursor;
} }
@ -183,50 +191,37 @@ pub fn getCursor() u16 {
/// Enables the blinking cursor to that is is visible. /// Enables the blinking cursor to that is is visible.
/// ///
pub fn enableCursor() void { pub fn enableCursor() void {
arch.outb(PORT_ADDRESS, REG_CURSOR_START); sendPortData(REG_CURSOR_START, cursor_scanline_start);
arch.outb(PORT_DATA, cursor_scanline_start); sendPortData(REG_CURSOR_END, cursor_scanline_end);
arch.outb(PORT_ADDRESS, REG_CURSOR_END);
arch.outb(PORT_DATA, cursor_scanline_end);
} }
/// ///
/// Disables the blinking cursor to that is is visible. /// Disables the blinking cursor to that is is visible.
/// ///
pub fn disableCursor() void { pub fn disableCursor() void {
arch.outb(PORT_ADDRESS, REG_CURSOR_START); sendPortData(REG_CURSOR_START, CURSOR_DISABLE);
arch.outb(PORT_DATA, CURSOR_DISABLE);
} }
/// ///
/// Set the shape of the cursor. This can be and underline or block shape. /// Set the shape of the cursor. This can be and underline or block shape.
/// ///
/// Arguments: /// Arguments:
/// IN shape: CURSOR_SHAPE - The enum CURSOR_SHAPE that selects which shape to use. /// IN shape: CursorShape - The enum CursorShape that selects which shape to use.
/// ///
pub fn setCursorShape(shape: CursorShape) void { pub fn setCursorShape(shape: CursorShape) void {
switch (shape) { switch (shape) {
CursorShape.UNDERLINE => { CursorShape.UNDERLINE => {
arch.outb(PORT_ADDRESS, REG_CURSOR_START);
arch.outb(PORT_DATA, CURSOR_SCANLINE_MIDDLE);
arch.outb(PORT_ADDRESS, REG_CURSOR_END);
arch.outb(PORT_DATA, CURSOR_SCANLINE_END);
cursor_scanline_start = CURSOR_SCANLINE_MIDDLE; cursor_scanline_start = CURSOR_SCANLINE_MIDDLE;
cursor_scanline_end = CURSOR_SCANLINE_END; cursor_scanline_end = CURSOR_SCANLINE_END;
}, },
CursorShape.BLOCK => { CursorShape.BLOCK => {
arch.outb(PORT_ADDRESS, REG_CURSOR_START);
arch.outb(PORT_DATA, CURSOR_SCANLINE_START);
arch.outb(PORT_ADDRESS, REG_CURSOR_END);
arch.outb(PORT_DATA, CURSOR_SCANLINE_END);
cursor_scanline_start = CURSOR_SCANLINE_START; cursor_scanline_start = CURSOR_SCANLINE_START;
cursor_scanline_end = CURSOR_SCANLINE_END; cursor_scanline_end = CURSOR_SCANLINE_END;
}, },
} }
sendPortData(REG_CURSOR_START, cursor_scanline_start);
sendPortData(REG_CURSOR_END, cursor_scanline_end);
} }
/// ///
@ -234,176 +229,8 @@ pub fn setCursorShape(shape: CursorShape) void {
/// ///
pub fn init() void { pub fn init() void {
// Set the maximum scan line to 0x0F // Set the maximum scan line to 0x0F
arch.outb(PORT_ADDRESS, REG_MAXIMUM_SCAN_LINE); sendPortData(REG_MAXIMUM_SCAN_LINE, CURSOR_SCANLINE_END);
arch.outb(PORT_DATA, CURSOR_SCANLINE_END);
// Set by default the underline cursor // Set by default the underline cursor
setCursorShape(CursorShape.UNDERLINE); setCursorShape(CursorShape.UNDERLINE);
} }
test "entryColour" {
var fg: u4 = COLOUR_BLACK;
var bg: u4 = COLOUR_BLACK;
var res: u8 = entryColour(fg, bg);
expectEqual(u8(0x00), res);
fg = COLOUR_LIGHT_GREEN;
bg = COLOUR_BLACK;
res = entryColour(fg, bg);
expectEqual(u8(0x0A), res);
fg = COLOUR_BLACK;
bg = COLOUR_LIGHT_GREEN;
res = entryColour(fg, bg);
expectEqual(u8(0xA0), res);
fg = COLOUR_BROWN;
bg = COLOUR_LIGHT_GREEN;
res = entryColour(fg, bg);
expectEqual(u8(0xA6), res);
}
test "entry" {
var colour: u8 = entryColour(COLOUR_BROWN, COLOUR_LIGHT_GREEN);
expectEqual(u8(0xA6), colour);
// Character '0' is 0x30
var video_entry: u16 = entry('0', colour);
expectEqual(u16(0xA630), video_entry);
video_entry = entry(0x55, colour);
expectEqual(u16(0xA655), video_entry);
}
fn testOutOfBounds(x: u16, y: u16) bool {
if (x < HEIGHT and y < WIDTH) {
return true;
}
return false;
}
fn testUpperVal(x: u16, y: u16) u16 {
const pos: u16 = x * WIDTH + y;
const pos_upper: u16 = (pos >> 8) & 0x00FF;
return pos_upper;
}
fn testLowerVal(x: u16, y: u16) u16 {
const pos: u16 = x * WIDTH + y;
const pos_lower: u16 = pos & 0x00FF;
return pos_lower;
}
test "updateCursor out of bounds" {
var x: u16 = 0;
var y: u16 = 0;
var res: bool = testOutOfBounds(x, y);
expectEqual(true, res);
x = HEIGHT - 1;
res = testOutOfBounds(x, y);
expectEqual(true, res);
y = WIDTH - 1;
res = testOutOfBounds(x, y);
expectEqual(true, res);
x = HEIGHT;
y = WIDTH;
res = testOutOfBounds(x, y);
expectEqual(false, res);
x = HEIGHT - 1;
y = WIDTH;
res = testOutOfBounds(x, y);
expectEqual(false, res);
x = HEIGHT;
y = WIDTH - 1;
res = testOutOfBounds(x, y);
expectEqual(false, res);
}
test "updateCursor lower values" {
var x: u16 = 0x0000;
var y: u16 = 0x0000;
var res: u16 = testLowerVal(x, y);
var expected: u16 = 0x0000;
expectEqual(expected, res);
x = 0x0000;
y = 0x000A;
res = testLowerVal(x, y);
expected = 0x000A;
expectEqual(expected, res);
x = 0x000A;
y = 0x0000;
res = testLowerVal(x, y);
expected = 0x0020;
expectEqual(expected, res);
x = 0x000A;
y = 0x000A;
res = testLowerVal(x, y);
expected = 0x002A;
expectEqual(expected, res);
}
test "updateCursor upper values" {
var x: u16 = 0x0000;
var y: u16 = 0x0000;
var res: u16 = testUpperVal(x, y);
var expected: u16 = 0x0000;
expectEqual(expected, res);
x = 0x0000;
y = 0x000A;
res = testUpperVal(x, y);
expected = 0x0000;
expectEqual(expected, res);
x = 0x000A;
y = 0x0000;
res = testUpperVal(x, y);
expected = 0x0003;
expectEqual(expected, res);
x = 0x000A;
y = 0x000A;
res = testUpperVal(x, y);
expected = 0x0003;
expectEqual(expected, res);
}
test "getCursor all" {
warn(" Waiting for mocking ");
var res = getCursor();
}
test "enableCursor all" {
warn(" Waiting for mocking ");
enableCursor();
}
test "disableCursor all" {
warn(" Waiting for mocking ");
disableCursor();
}
test "setCursorShape all" {
setCursorShape(CursorShape.UNDERLINE);
expectEqual(CURSOR_SCANLINE_MIDDLE, cursor_scanline_start);
expectEqual(CURSOR_SCANLINE_END, cursor_scanline_end);
setCursorShape(CursorShape.BLOCK);
expectEqual(CURSOR_SCANLINE_START, cursor_scanline_start);
expectEqual(CURSOR_SCANLINE_END, cursor_scanline_end);
}
test "init all" {
warn(" Waiting for mocking ");
init();
expectEqual(CURSOR_SCANLINE_MIDDLE, cursor_scanline_start);
expectEqual(CURSOR_SCANLINE_END, cursor_scanline_end);
}

View file

@ -1,41 +0,0 @@
// Zig version: 0.4.0
///
/// Initialise the architecture
///
pub fn init() void {}
///
/// Inline assembly to write to a given port with a byte of data.
///
/// Arguments:
/// IN port: u16 - The port to write to.
/// IN data: u8 - The byte of data that will be sent.
///
pub fn outb(port: u16, data: u8) void {}
///
/// Inline assembly that reads data from a given port and returns its value.
///
/// Arguments:
/// IN port: u16 - The port to read data from.
///
/// Return:
/// The data that the port returns.
///
pub fn inb(port: u16) u8 {return 0;}
///
/// A simple way of waiting for I/O event to happen by doing an I/O event to flush the I/O
/// event being waited.
///
pub fn ioWait() void {}
///
/// Register an interrupt handler. The interrupt number should be the arch-specific number.
///
/// Arguments:
/// IN int: u16 - The arch-specific interrupt number to register for.
/// IN handler: fn (ctx: *InterruptContext) void - The handler to assign to the interrupt.
///
pub fn registerInterruptHandler(int: u16, ctx: fn (ctx: *InterruptContext) void) void {}

View file

@ -0,0 +1,94 @@
const std = @import("std");
const MemProfile = @import("mem_mock.zig").MemProfile;
const expect = std.testing.expect;
const warn = std.debug.warn;
const mock_framework = @import("mock_framework.zig");
pub const initTest = mock_framework.initTest;
pub const freeTest = mock_framework.freeTest;
pub const addTestParams = mock_framework.addTestParams;
pub const addConsumeFunction = mock_framework.addConsumeFunction;
pub const addRepeatFunction = mock_framework.addRepeatFunction;
pub const InterruptContext = struct {
// Extra segments
gs: u32,
fs: u32,
es: u32,
ds: u32,
// Destination, source, base pointer
edi: u32,
esi: u32,
ebp: u32,
esp: u32,
// General registers
ebx: u32,
edx: u32,
ecx: u32,
eax: u32,
// Interrupt number and error code
int_num: u32,
error_code: u32,
// Instruction pointer, code segment and flags
eip: u32,
cs: u32,
eflags: u32,
user_esp: u32,
ss: u32,
};
pub fn init(mem_profile: *const MemProfile, allocator: *std.mem.Allocator, comptime options: type) void {
//return mock_framework.performAction("init", void, mem_profile, allocator);
}
pub fn outb(port: u16, data: u8) void {
return mock_framework.performAction("outb", void, port, data);
}
pub fn inb(port: u16) u8 {
return mock_framework.performAction("inb", u8, port);
}
pub fn ioWait() void {
return mock_framework.performAction("ioWait", void);
}
pub fn registerInterruptHandler(int: u16, ctx: fn (ctx: *InterruptContext) void) void {
return mock_framework.performAction("registerInterruptHandler", void, int, ctx);
}
pub fn lgdt(gdt_ptr: *const gdt.GdtPtr) void {
return mock_framework.performAction("lgdt", void, gdt_ptr.*);
}
pub fn ltr() void {
return mock_framework.performAction("ltr", void);
}
pub fn lidt(idt_ptr: *const idt.IdtPtr) void {
return mock_framework.performAction("lidt", void, idt_ptr.*);
}
pub fn enableInterrupts() void {
return mock_framework.performAction("enableInterrupts", void);
}
pub fn disableInterrupts() void {
return mock_framework.performAction("disableInterrupts", void);
}
pub fn halt() void {
return mock_framework.performAction("halt", void);
}
pub fn spinWait() noreturn {
while (true) {}
}
pub fn haltNoInterrupts() noreturn {
while (true) {}
}

View file

@ -0,0 +1,50 @@
const mock_framework = @import("mock_framework.zig");
pub const Level = enum {
INFO,
DEBUG,
WARNING,
ERROR
};
fn logCallback(context: void, str: []const u8) anyerror!void {}
pub fn log(comptime level: Level, comptime format: []const u8, args: ...) void {
//return mock_framework.performAction("log", void, level, format, args);
}
pub fn logInfo(comptime format: []const u8, args: ...) void {
//return mock_framework.performAction("logInfo", void, format, args);
}
pub fn logDebug(comptime format: []const u8, args: ...) void {
//return mock_framework.performAction("logDebug", void, format, args);
}
pub fn logWarning(comptime format: []const u8, args: ...) void {
//return mock_framework.performAction("logWarning", void, format, args);
}
pub fn logError(comptime format: []const u8, args: ...) void {
//return mock_framework.performAction("logError", void, format, args);
}
pub fn addRepeatFunction(comptime fun_name: []const u8, function: var) void {
mock_framework.addRepeatFunction(fun_name, function);
}
pub fn addTestFunction(comptime fun_name: []const u8, function: var) void {
mock_framework.addRepeatFunction(fun_name, function);
}
pub fn addTestParams(comptime fun_name: []const u8, params: ...) void {
mock_framework.addTestParams(fun_name, params);
}
pub fn initTest() void {
mock_framework.initTest();
}
pub fn freeTest() void {
mock_framework.freeTest();
}

View file

@ -0,0 +1,32 @@
const multiboot = @import("../../../src/kernel/multiboot.zig");
pub const MemProfile = struct {
vaddr_end: [*]u8,
vaddr_start: [*]u8,
physaddr_end: [*]u8,
physaddr_start: [*]u8,
mem_kb: u32,
fixed_alloc_size: u32
};
// The virtual/physical start/end of the kernel code
var KERNEL_PHYSADDR_START: u32 = 0x00100000;
var KERNEL_PHYSADDR_END: u32 = 0x01000000;
var KERNEL_VADDR_START: u32 = 0xC0100000;
var KERNEL_VADDR_END: u32 = 0xC1100000;
var KERNEL_ADDR_OFFSET: u32 = 0xC0000000;
// The size of the fixed allocator used before the heap is set up. Set to 1MiB.
const FIXED_ALLOC_SIZE = 1024 * 1024;
pub fn init(mb_info: *multiboot.multiboot_info_t) MemProfile {
return MemProfile{
.vaddr_end = @ptrCast([*]u8, &KERNEL_VADDR_END),
.vaddr_start = @ptrCast([*]u8, &KERNEL_VADDR_START),
.physaddr_end = @ptrCast([*]u8, &KERNEL_PHYSADDR_END),
.physaddr_start = @ptrCast([*]u8, &KERNEL_PHYSADDR_START),
// Total memory available including the initial 1MiB that grub doesn't include
.mem_kb = mb_info.mem_upper + mb_info.mem_lower + 1024,
.fixed_alloc_size = FIXED_ALLOC_SIZE
};
}

View file

@ -0,0 +1,589 @@
const std = @import("std");
const builtin = @import("builtin");
const StringHashMap = std.StringHashMap;
const expect = std.testing.expect;
const expectEqual = std.testing.expectEqual;
const GlobalAllocator = std.debug.global_allocator;
const TailQueue = std.TailQueue;
const warn = std.debug.warn;
///
/// The enumeration of types that the mocking framework supports. These include basic types like u8
/// and function types like fn () void
///
const DataElementType = enum {
U4,
U8,
U16,
U32,
FN_OVOID,
FN_OUSIZE,
FN_OU16,
FN_IU16_OU8,
FN_IU4_IU4_OU8,
FN_IU8_IU8_OU16,
FN_IU16_IU8_OVOID,
FN_IU16_IU16_OVOID,
};
///
/// A tagged union of all the data elements that the mocking framework can work with. This can be
/// expanded to add new types. This is needed as need a list of data that all have different types,
/// so this wraps the data into a union, (which is of one type) so can have a list of them.
///
const DataElement = union(DataElementType) {
U4: u4,
U8: u8,
U16: u16,
U32: u32,
FN_OVOID: fn () void,
FN_OUSIZE: fn () usize,
FN_OU16: fn () u16,
FN_IU16_OU8: fn (u16) u8,
FN_IU4_IU4_OU8: fn (u4, u4) u8,
FN_IU8_IU8_OU16: fn (u8, u8) u16,
FN_IU16_IU8_OVOID: fn (u16, u8) void,
FN_IU16_IU16_OVOID: fn (u16, u16) void,
};
///
/// The type of actions that the mocking framework can perform.
///
const ActionType = enum {
/// This will test the parameters passed to a function. It will test the correct types and
/// value of each parameter. This is also used to return a specific value from a function so
/// can test for returns from a function.
TestValue,
/// This action is to replace a function call to be mocked with another function the user
/// chooses to be replaced. This will consume the function call. This will allow the user to
/// check that the function is called once or multiple times by added a function to be mocked
/// multiple times. This also allows the ability for a function to be mocked by different
/// functions each time it is called.
ConsumeFunctionCall,
/// This is similar to the ConsumeFunctionCall action, but will call the mocked function
/// repeatedly until the mocking is done.
RepeatFunctionCall,
// Other actions that could be used
// This will check that a function isn't called.
//NoFunctionCall
// This is a generalisation of ConsumeFunctionCall and RepeatFunctionCall but can specify how
// many times a function can be called.
//FunctionCallN
};
///
/// This is a pair of action and data to be actioned on.
///
const Action = struct {
action: ActionType,
data: DataElement,
};
///
/// The type for a queue of actions using std.TailQueue.
///
const ActionList = TailQueue(Action);
///
/// The type for linking the function name to be mocked and the action list to be acted on.
///
const NamedActionMap = StringHashMap(ActionList);
///
/// The mocking framework.
///
/// Return: type
/// This returns a struct for adding and acting on mocked functions.
///
fn Mock() type {
return struct {
const Self = @This();
/// The map of function name and action list.
named_actions: NamedActionMap,
///
/// Create a DataElement from data. This wraps data into a union. This allows the ability
/// to have a list of different types.
///
/// Arguments:
/// IN arg: var - The data, this can be a function or basic type value.
///
/// Return: DataElement
/// A DataElement with the data wrapped.
///
fn createDataElement(arg: var) DataElement {
return switch (@typeOf(arg)) {
u4 => DataElement{ .U4 = arg },
u8 => DataElement{ .U8 = arg },
u16 => DataElement{ .U16 = arg },
u32 => DataElement{ .U32 = arg },
fn () void => DataElement{ .FN_OVOID = arg },
fn () usize => DataElement{ .FN_OUSIZE = arg },
fn () u16 => DataElement{ .FN_OU16 = arg },
fn (u16) u8 => DataElement{ .FN_IU16_OU8 = arg },
fn (u4, u4) u8 => DataElement{ .FN_IU4_IU4_OU8 = arg },
fn (u8, u8) u16 => DataElement{ .FN_IU8_IU8_OU16 = arg },
fn (u16, u8) void => DataElement{ .FN_IU16_IU8_OVOID = arg },
fn (u16, u16) void => DataElement{ .FN_IU16_IU16_OVOID = arg },
else => @compileError("Type not supported: " ++ @typeName(@typeOf(arg))),
};
}
///
/// Get the enum that represents the type given.
///
/// Arguments:
/// IN T: type - A type.
///
/// Return: DataElementType
/// The DataElementType that represents the type given.
///
fn getDataElementType(comptime T: type) DataElementType {
return switch (T) {
u4 => DataElementType.U4,
u8 => DataElementType.U8,
u16 => DataElementType.U16,
u32 => DataElementType.U32,
fn () void => DataElementType.FN_OVOID,
fn () u16 => DataElementType.FN_OU16,
fn (u16) u8 => DataElementType.FN_IU16_OU8,
fn (u4, u4) u8 => DataElementType.FN_IU4_IU4_OU8,
fn (u8, u8) u16 => DataElementType.FN_IU8_IU8_OU16,
fn (u16, u8) void => DataElementType.FN_IU16_IU8_OVOID,
fn (u16, u16) void => DataElementType.FN_IU16_IU16_OVOID,
else => @compileError("Type not supported: " ++ @typeName(T)),
};
}
///
/// Get the data out of the tagged union
///
/// Arguments:
/// IN T: type - The type of the data to extract. Used to switch on the
/// tagged union.
/// IN element: DataElement - The data element to unwrap the data from.
///
/// Return: T
/// The data of type T from the DataElement.
///
fn getDataValue(comptime T: type, element: DataElement) T {
return switch (T) {
u4 => element.U4,
u8 => element.U8,
u16 => element.U16,
u32 => element.U32,
fn () void => element.FN_OVOID,
fn () u16 => element.FN_OU16,
fn (u16) u8 => element.FN_IU16_OU8,
fn (u4, u4) u8 => element.FN_IU4_IU4_OU8,
fn (u8, u8) u16 => element.FN_IU8_IU8_OU16,
fn (u16, u8) void => element.FN_IU16_IU8_OVOID,
fn (u16, u16) void => element.FN_IU16_IU16_OVOID,
else => @compileError("Type not supported: " ++ @typeName(T)),
};
}
///
/// Create a function type from a return type and its arguments. Waiting for
/// https://github.com/ziglang/zig/issues/313. TODO: Tidy mocking framework #69
///
/// Arguments:
/// IN RetType: type - The return type of the function.
/// IN params: arglist - The argument list for the function.
///
/// Return: type
/// A function type that represents the return type and its arguments.
///
fn getFunctionType(comptime RetType: type, params: ...) type {
return switch (params.len) {
0 => fn () RetType,
1 => fn (@typeOf(params[0])) RetType,
2 => fn (@typeOf(params[0]), @typeOf(params[1])) RetType,
else => @compileError("Couldn't generate function type for " ++ params.len ++ "parameters\n"),
};
}
///
/// This tests a value passed to a function.
///
/// Arguments:
/// IN ExpectedType: type - The expected type of the value to be tested.
/// IN expected_value: ExpectedType - The expected value to be tested. This is what was
/// passed to the functions.
/// IN elem: DataElement - The wrapped data element to test against the
/// expected value.
///
fn expectTest(comptime ExpectedType: type, expected_value: ExpectedType, elem: DataElement) void {
if (ExpectedType == void) {
// Can't test void as it has no value
warn("Can not test a value for void\n");
expect(false);
}
// Test that the types match
const expect_type = comptime getDataElementType(ExpectedType);
expectEqual(expect_type, DataElementType(elem));
// Types match, so can use the expected type to get the actual data
const actual_value = getDataValue(ExpectedType, elem);
// Test the values
expectEqual(expected_value, actual_value);
}
///
/// This returns a value from the wrapped data element. This will be a test value to be
/// returned by a mocked function.
///
/// Arguments:
/// IN fun_name: []const u8 - The function name to be used to tell the user if
/// there is no return value set up.
/// IN/OUT action_list: *ActionList - The action list to extract the return value from.
/// IN DataType: type - The type of the return value.
///
fn expectGetValue(comptime fun_name: []const u8, action_list: *ActionList, comptime DataType: type) DataType {
if (DataType == void) {
return;
}
if (action_list.*.popFirst()) |action_node| {
const action = action_node.data;
const expect_type = getDataElementType(DataType);
const ret = getDataValue(DataType, action.data);
expectEqual(DataElementType(action.data), expect_type);
// Free the node
action_list.*.destroyNode(action_node, GlobalAllocator);
return ret;
} else {
warn("No more test values for the return of function: " ++ fun_name ++ "\n");
expect(false);
unreachable;
}
}
///
/// This adds a action to the action list with ActionType provided. It will create a new
/// mapping if one doesn't exist for a function name.
///
/// Arguments:
/// IN/OUT self: *Self - Self. This is the mocking object to be modified to add
/// the test data.
/// IN fun_name: []const u8 - The function name to add the test parameters to.
/// IN data: var - The data to add.
/// IN action_type: ActionType - The action type to add.
///
pub fn addAction(self: *Self, comptime fun_name: []const u8, data: var, action_type: ActionType) void {
// Add a new mapping if one doesn't exist.
if (!self.named_actions.contains(fun_name)) {
expect(self.named_actions.put(fun_name, TailQueue(Action).init()) catch unreachable == null);
}
// Get the function mapping to add the parameter to.
if (self.named_actions.get(fun_name)) |actions_kv| {
var action_list = actions_kv.value;
const action = Action{
.action = action_type,
.data = createDataElement(data),
};
var a = action_list.createNode(action, GlobalAllocator) catch unreachable;
action_list.append(a);
// Need to re-assign the value as it isn't updated when we just append
actions_kv.value = action_list;
} else {
// Shouldn't get here as we would have just added a new mapping
// But just in case ;)
warn("No function name: " ++ fun_name ++ "\n");
expect(false);
unreachable;
}
}
///
/// Perform an action on a function. This can be one of ActionType.
///
/// Arguments:
/// IN/OUT self: *Self - Self. This is the mocking object to be modified to
/// perform a action.
/// IN fun_name: []const u8 - The function name to act on.
/// IN RetType: type - The return type of the function being mocked.
/// IN params: arglist - The list of parameters of the mocked function.
///
/// Return: RetType
/// The return value of the mocked function. This can be void.
///
pub fn performAction(self: *Self, comptime fun_name: []const u8, comptime RetType: type, params: ...) RetType {
if (self.named_actions.get(fun_name)) |kv_actions_list| {
var action_list = kv_actions_list.value;
// Peak the first action to test the action type
if (action_list.first) |action_node| {
const action = action_node.data;
const ret = switch (action.action) {
ActionType.TestValue => ret: {
comptime var i = 0;
inline while (i < params.len) : (i += 1) {
// Now pop the action as we are going to use it
// Have already checked that it is not null
const test_node = action_list.popFirst().?;
const test_action = test_node.data;
const param = params[i];
const param_type = @typeOf(params[i]);
expectTest(param_type, param, test_action.data);
// Free the node
action_list.destroyNode(test_node, GlobalAllocator);
}
break :ret expectGetValue(fun_name, &action_list, RetType);
},
ActionType.ConsumeFunctionCall => ret: {
// Now pop the action as we are going to use it
// Have already checked that it is not null
const test_node = action_list.popFirst().?;
const test_element = test_node.data.data;
// Work out the type of the function to call from the params and return type
// At compile time
//const expected_function = getFunctionType(RetType, params);
// Waiting for this:
// error: compiler bug: unable to call var args function at compile time. https://github.com/ziglang/zig/issues/313
// to be resolved
const expected_function = switch (params.len) {
0 => fn () RetType,
1 => fn (@typeOf(params[0])) RetType,
2 => fn (@typeOf(params[0]), @typeOf(params[1])) RetType,
else => @compileError("Couldn't generate function type for " ++ params.len ++ "parameters\n"),
};
// Get the corresponding DataElementType
const expect_type = comptime getDataElementType(expected_function);
// Test that the types match
expectEqual(expect_type, DataElementType(test_element));
// Types match, so can use the expected type to get the actual data
const actual_function = getDataValue(expected_function, test_element);
// Free the node
action_list.destroyNode(test_node, GlobalAllocator);
// The data element will contain the function to call
const r = switch (params.len) {
0 => @noInlineCall(actual_function),
1 => @noInlineCall(actual_function, params[0]),
2 => @noInlineCall(actual_function, params[0], params[1]),
else => @compileError(params.len ++ " or more parameters not supported"),
};
break :ret r;
},
ActionType.RepeatFunctionCall => ret: {
// Do the same for ActionType.ConsumeFunctionCall but instead of
// popping the function, just peak
const test_element = action.data;
const expected_function = switch (params.len) {
0 => fn () RetType,
1 => fn (@typeOf(params[0])) RetType,
2 => fn (@typeOf(params[0]), @typeOf(params[1])) RetType,
else => @compileError("Couldn't generate function type for " ++ params.len ++ "parameters\n"),
};
// Get the corresponding DataElementType
const expect_type = comptime getDataElementType(expected_function);
// Test that the types match
expectEqual(expect_type, DataElementType(test_element));
// Types match, so can use the expected type to get the actual data
const actual_function = getDataValue(expected_function, test_element);
// The data element will contain the function to call
const r = switch (params.len) {
0 => @noInlineCall(actual_function),
1 => @noInlineCall(actual_function, params[0]),
2 => @noInlineCall(actual_function, params[0], params[1]),
else => @compileError(params.len ++ " or more parameters not supported"),
};
break :ret r;
},
};
// Re-assign the action list as this would have changed
kv_actions_list.value = action_list;
return ret;
} else {
warn("No action list elements for function: " ++ fun_name ++ "\n");
expect(false);
unreachable;
}
} else {
warn("No function name: " ++ fun_name ++ "\n");
expect(false);
unreachable;
}
}
///
/// Initialise the mocking framework.
///
/// Return: Self
/// An initialised mocking framework.
///
pub fn init() Self {
return Self{
.named_actions = StringHashMap(ActionList).init(GlobalAllocator),
};
}
///
/// End the mocking session. This will check all test parameters and consume functions are
/// consumed. Any repeat functions are deinit.
///
/// Arguments:
/// IN/OUT self: *Self - Self. This is the mocking object to be modified to finished
/// the mocking session.
///
pub fn finish(self: *Self) void {
// Make sure the expected list is empty
var it = self.named_actions.iterator();
while (it.next()) |next| {
var action_list = next.value;
if (action_list.popFirst()) |action_node| {
const action = action_node.data;
switch (action.action) {
ActionType.TestValue, ActionType.ConsumeFunctionCall => {
// These need to be all consumed
warn("Unused testing value: Type: {}, value: {} for function '{}'\n", action.action, DataElementType(action.data), next.key);
expect(false);
unreachable;
},
ActionType.RepeatFunctionCall => {
// As this is a repeat action, the function will still be here
// So need to free it
action_list.destroyNode(action_node, GlobalAllocator);
next.value = action_list;
},
}
}
}
// Free the function mapping
self.named_actions.deinit();
}
};
}
/// The global mocking object that is used for a mocking session. Maybe in the future, we can have
/// local mocking objects so can run the tests in parallel.
var mock: ?Mock() = null;
///
/// Get the mocking object and check we have one initialised.
///
/// Return: *Mock()
/// Pointer to the global mocking object so can be modified.
///
fn getMockObject() *Mock() {
// Make sure we have a mock object
if (mock) |*m| {
return m;
} else {
warn("MOCK object doesn't exists, please initiate this test\n");
expect(false);
unreachable;
}
}
///
/// Initialise the mocking framework.
///
pub fn initTest() void {
// Make sure there isn't a mock object
if (mock) |_| {
warn("MOCK object already exists, please free previous test\n");
expect(false);
unreachable;
} else {
mock = Mock().init();
}
}
///
/// End the mocking session. This will check all test parameters and consume functions are
/// consumed. Any repeat functions are deinit.
///
pub fn freeTest() void {
getMockObject().finish();
// This will stop double frees
mock = null;
}
///
/// Add a list of test parameters to the action list. This will create a list of data
/// elements that represent the list of parameters that will be passed to a mocked
/// function. A mocked function may be called multiple times, so this list may contain
/// multiple values for each call to the same mocked function.
///
/// Arguments:
/// IN/OUT self: *Self - Self. This is the mocking object to be modified to add
/// the test parameters.
/// IN fun_name: []const u8 - The function name to add the test parameters to.
/// IN params: arglist - The parameters to add.
///
pub fn addTestParams(comptime fun_name: []const u8, params: ...) void {
var mock_obj = getMockObject();
comptime var i = 0;
inline while (i < params.len) : (i += 1) {
mock_obj.addAction(fun_name, params[i], ActionType.TestValue);
}
}
///
/// Add a function to mock out another. This will add a consume function action, so once
/// the mocked function is called, this action wil be removed.
///
/// Arguments:
/// IN fun_name: []const u8 - The function name to add the function to.
/// IN function: var - The function to add.
///
pub fn addConsumeFunction(comptime fun_name: []const u8, function: var) void {
getMockObject().addAction(fun_name, function, ActionType.ConsumeFunctionCall);
}
///
/// Add a function to mock out another. This will add a repeat function action, so once
/// the mocked function is called, this action wil be removed.
///
/// Arguments:
/// IN fun_name: []const u8 - The function name to add the function to.
/// IN function: var - The function to add.
///
pub fn addRepeatFunction(comptime fun_name: []const u8, function: var) void {
getMockObject().addAction(fun_name, function, ActionType.RepeatFunctionCall);
}
///
/// Perform an action on a function. This can be one of ActionType.
///
/// Arguments:
/// IN fun_name: []const u8 - The function name to act on.
/// IN RetType: type - The return type of the function being mocked.
/// IN params: arglist - The list of parameters of the mocked function.
///
/// Return: RetType
/// The return value of the mocked function. This can be void.
///
pub fn performAction(comptime fun_name: []const u8, comptime RetType: type, params: ...) RetType {
return getMockObject().performAction(fun_name, RetType, params);
}

View file

@ -0,0 +1,5 @@
pub const log = @import("log_mock.zig");
pub const mem = @import("mem_mock.zig");
pub const vga = @import("vga_mock.zig");
pub const arch = @import("arch_mock.zig");
pub const panic = @import("panic_mock.zig");

View file

@ -0,0 +1,7 @@
const builtin = @import("builtin");
const panic = @import("std").debug.panic;
pub fn panicFmt(trace: ?*builtin.StackTrace, comptime format: []const u8, args: ...) noreturn {
@setCold(true);
panic(format, args);
}

View file

@ -0,0 +1,88 @@
const std = @import("std");
const expect = std.testing.expect;
const arch = @import("arch.zig").internals;
const mock_framework = @import("mock_framework.zig");
pub const initTest = mock_framework.initTest;
pub const freeTest = mock_framework.freeTest;
pub const addTestParams = mock_framework.addTestParams;
pub const addConsumeFunction = mock_framework.addConsumeFunction;
pub const addRepeatFunction = mock_framework.addRepeatFunction;
pub const WIDTH: u16 = 80;
pub const HEIGHT: u16 = 25;
pub const COLOUR_BLACK: u4 = 0x00;
pub const COLOUR_BLUE: u4 = 0x01;
pub const COLOUR_GREEN: u4 = 0x02;
pub const COLOUR_CYAN: u4 = 0x03;
pub const COLOUR_RED: u4 = 0x04;
pub const COLOUR_MAGENTA: u4 = 0x05;
pub const COLOUR_BROWN: u4 = 0x06;
pub const COLOUR_LIGHT_GREY: u4 = 0x07;
pub const COLOUR_DARK_GREY: u4 = 0x08;
pub const COLOUR_LIGHT_BLUE: u4 = 0x09;
pub const COLOUR_LIGHT_GREEN: u4 = 0x0A;
pub const COLOUR_LIGHT_CYAN: u4 = 0x0B;
pub const COLOUR_LIGHT_RED: u4 = 0x0C;
pub const COLOUR_LIGHT_MAGENTA: u4 = 0x0D;
pub const COLOUR_LIGHT_BROWN: u4 = 0x0E;
pub const COLOUR_WHITE: u4 = 0x0F;
pub const CursorShape = enum {
UNDERLINE,
BLOCK,
};
pub fn entryColour(fg: u4, bg: u4) u8 {
return mock_framework.performAction("entryColour", u8, fg, bg);
}
pub fn entry(uc: u8, colour: u8) u16 {
return mock_framework.performAction("entry", u16, uc, colour);
}
pub fn updateCursor(x: u16, y: u16) void {
return mock_framework.performAction("updateCursor", void, x, y);
}
pub fn getCursor() u16 {
return mock_framework.performAction("getCursor", u16);
}
pub fn enableCursor() void {
return mock_framework.performAction("enableCursor", void);
}
pub fn disableCursor() void {
return mock_framework.performAction("disableCursor", void);
}
pub fn setCursorShape(shape: CursorShape) void {
return mock_framework.performAction("setCursorShape", void, shape);
}
pub fn init() void {
return mock_framework.performAction("init", void);
}
// User defined mocked functions
pub fn mock_entryColour(fg: u4, bg: u4) u8 {
return u8(fg) | u8(bg) << 4;
}
pub fn mock_entry(uc: u8, c: u8) u16 {
return u16(uc) | u16(c) << 8;
}
pub fn mock_updateCursor(x: u16, y: u16) void {
// Here we can do any testing we like with the parameters. e.g. test out of bounds
expect(x < WIDTH);
expect(y < HEIGHT);
}
pub fn mock_enableCursor() void {}
pub fn mock_disableCursor() void {}

View file

@ -0,0 +1,4 @@
// I gave up on trying to get all the tests in a separate file for the tty
test "" {
_ = @import("../../../src/kernel/tty.zig");
}

View file

@ -0,0 +1,293 @@
const vga = @import("../../../src/kernel/vga.zig");
const arch = @import("../../../src/kernel/arch.zig").internals;
const expectEqual = @import("std").testing.expectEqual;
test "entryColour" {
var fg: u4 = vga.COLOUR_BLACK;
var bg: u4 = vga.COLOUR_BLACK;
var res: u8 = vga.entryColour(fg, bg);
expectEqual(u8(0x00), res);
fg = vga.COLOUR_LIGHT_GREEN;
bg = vga.COLOUR_BLACK;
res = vga.entryColour(fg, bg);
expectEqual(u8(0x0A), res);
fg = vga.COLOUR_BLACK;
bg = vga.COLOUR_LIGHT_GREEN;
res = vga.entryColour(fg, bg);
expectEqual(u8(0xA0), res);
fg = vga.COLOUR_BROWN;
bg = vga.COLOUR_LIGHT_GREEN;
res = vga.entryColour(fg, bg);
expectEqual(u8(0xA6), res);
}
test "entry" {
var colour: u8 = vga.entryColour(vga.COLOUR_BROWN, vga.COLOUR_LIGHT_GREEN);
expectEqual(u8(0xA6), colour);
// Character '0' is 0x30
var video_entry: u16 = vga.entry('0', colour);
expectEqual(u16(0xA630), video_entry);
video_entry = vga.entry(0x55, colour);
expectEqual(u16(0xA655), video_entry);
}
test "updateCursor width out of bounds" {
const x: u16 = vga.WIDTH;
const y: u16 = 0;
const max_cursor: u16 = (vga.HEIGHT - 1) * vga.WIDTH + (vga.WIDTH - 1);
const expected_upper: u8 = @truncate(u8, (max_cursor >> 8) & 0x00FF);
const expected_lower: u8 = @truncate(u8, max_cursor & 0x00FF);
arch.initTest();
defer arch.freeTest();
// Mocking out the arch.outb calls for changing the hardware cursor:
arch.addTestParams("outb",
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_LOW,
vga.PORT_DATA, expected_lower,
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_HIGH,
vga.PORT_DATA, expected_upper);
vga.updateCursor(x, y);
}
test "updateCursor height out of bounds" {
const x: u16 = 0;
const y: u16 = vga.HEIGHT;
const max_cursor: u16 = (vga.HEIGHT - 1) * vga.WIDTH + (vga.WIDTH - 1);
const expected_upper: u8 = @truncate(u8, (max_cursor >> 8) & 0x00FF);
const expected_lower: u8 = @truncate(u8, max_cursor & 0x00FF);
arch.initTest();
defer arch.freeTest();
// Mocking out the arch.outb calls for changing the hardware cursor:
arch.addTestParams("outb",
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_LOW,
vga.PORT_DATA, expected_lower,
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_HIGH,
vga.PORT_DATA, expected_upper);
vga.updateCursor(x, y);
}
test "updateCursor width and height out of bounds" {
const x: u16 = vga.WIDTH;
const y: u16 = vga.HEIGHT;
const max_cursor: u16 = (vga.HEIGHT - 1) * vga.WIDTH + (vga.WIDTH - 1);
const expected_upper: u8 = @truncate(u8, (max_cursor >> 8) & 0x00FF);
const expected_lower: u8 = @truncate(u8, max_cursor & 0x00FF);
arch.initTest();
defer arch.freeTest();
// Mocking out the arch.outb calls for changing the hardware cursor:
arch.addTestParams("outb",
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_LOW,
vga.PORT_DATA, expected_lower,
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_HIGH,
vga.PORT_DATA, expected_upper);
vga.updateCursor(x, y);
}
test "updateCursor width-1 and height out of bounds" {
const x: u16 = vga.WIDTH - 1;
const y: u16 = vga.HEIGHT;
const max_cursor: u16 = (vga.HEIGHT - 1) * vga.WIDTH + (vga.WIDTH - 1);
const expected_upper: u8 = @truncate(u8, (max_cursor >> 8) & 0x00FF);
const expected_lower: u8 = @truncate(u8, max_cursor & 0x00FF);
arch.initTest();
defer arch.freeTest();
// Mocking out the arch.outb calls for changing the hardware cursor:
arch.addTestParams("outb",
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_LOW,
vga.PORT_DATA, expected_lower,
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_HIGH,
vga.PORT_DATA, expected_upper);
vga.updateCursor(x, y);
}
test "updateCursor width and height-1 out of bounds" {
const x: u16 = vga.WIDTH;
const y: u16 = vga.HEIGHT - 1;
const max_cursor: u16 = (vga.HEIGHT - 1) * vga.WIDTH + (vga.WIDTH - 1);
const expected_upper: u8 = @truncate(u8, (max_cursor >> 8) & 0x00FF);
const expected_lower: u8 = @truncate(u8, max_cursor & 0x00FF);
arch.initTest();
defer arch.freeTest();
// Mocking out the arch.outb calls for changing the hardware cursor:
arch.addTestParams("outb",
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_LOW,
vga.PORT_DATA, expected_lower,
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_HIGH,
vga.PORT_DATA, expected_upper);
vga.updateCursor(x, y);
}
test "updateCursor in bounds" {
var x: u16 = 0x000A;
var y: u16 = 0x000A;
const expected: u16 = y * vga.WIDTH + x;
var expected_upper: u8 = @truncate(u8, (expected >> 8) & 0x00FF);
var expected_lower: u8 = @truncate(u8, expected & 0x00FF);
arch.initTest();
defer arch.freeTest();
// Mocking out the arch.outb calls for changing the hardware cursor:
arch.addTestParams("outb",
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_LOW,
vga.PORT_DATA, expected_lower,
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_HIGH,
vga.PORT_DATA, expected_upper);
vga.updateCursor(x, y);
}
test "getCursor 1: 10" {
const expect: u16 = u16(10);
// Mocking out the arch.outb and arch.inb calls for getting the hardware cursor:
arch.initTest();
defer arch.freeTest();
arch.addTestParams("outb",
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_LOW);
arch.addTestParams("inb",
vga.PORT_DATA, u8(10));
arch.addTestParams("outb",
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_HIGH);
arch.addTestParams("inb",
vga.PORT_DATA, u8(0));
const actual: u16 = vga.getCursor();
expectEqual(expect, actual);
}
test "getCursor 2: 0xBEEF" {
const expect: u16 = u16(0xBEEF);
// Mocking out the arch.outb and arch.inb calls for getting the hardware cursor:
arch.initTest();
defer arch.freeTest();
arch.addTestParams("outb",
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_LOW);
arch.addTestParams("inb",
vga.PORT_DATA, u8(0xEF));
arch.addTestParams("outb",
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_HIGH);
arch.addTestParams("inb",
vga.PORT_DATA, u8(0xBE));
const actual: u16 = vga.getCursor();
expectEqual(expect, actual);
}
test "enableCursor all" {
arch.initTest();
defer arch.freeTest();
// Need to init the cursor start and end positions, so call the vga.init() to set this up
arch.addTestParams("outb",
vga.PORT_ADDRESS, vga.REG_MAXIMUM_SCAN_LINE,
vga.PORT_DATA, vga.CURSOR_SCANLINE_END,
vga.PORT_ADDRESS, vga.REG_CURSOR_START,
vga.PORT_DATA, vga.CURSOR_SCANLINE_MIDDLE,
vga.PORT_ADDRESS, vga.REG_CURSOR_END,
vga.PORT_DATA, vga.CURSOR_SCANLINE_END,
// Mocking out the arch.outb calls for enabling the cursor:
// These are the default cursor positions from vga.init()
vga.PORT_ADDRESS, vga.REG_CURSOR_START,
vga.PORT_DATA, vga.CURSOR_SCANLINE_MIDDLE,
vga.PORT_ADDRESS, vga.REG_CURSOR_END,
vga.PORT_DATA, vga.CURSOR_SCANLINE_END);
vga.init();
vga.enableCursor();
}
test "disableCursor all" {
arch.initTest();
defer arch.freeTest();
// Mocking out the arch.outb calls for disabling the cursor:
arch.addTestParams("outb",
vga.PORT_ADDRESS, vga.REG_CURSOR_START,
vga.PORT_DATA, vga.CURSOR_DISABLE);
vga.disableCursor();
}
test "setCursorShape UNDERLINE" {
arch.initTest();
defer arch.freeTest();
// Mocking out the arch.outb calls for setting the cursor shape to underline:
// This will also check that the scan line variables were set properly as these are using in
// the arch.outb call
arch.addTestParams("outb",
vga.PORT_ADDRESS, vga.REG_CURSOR_START,
vga.PORT_DATA, vga.CURSOR_SCANLINE_MIDDLE,
vga.PORT_ADDRESS, vga.REG_CURSOR_END,
vga.PORT_DATA, vga.CURSOR_SCANLINE_END);
vga.setCursorShape(vga.CursorShape.UNDERLINE);
}
test "setCursorShape BLOCK" {
arch.initTest();
defer arch.freeTest();
// Mocking out the arch.outb calls for setting the cursor shape to block:
// This will also check that the scan line variables were set properly as these are using in
// the arch.outb call
arch.addTestParams("outb",
vga.PORT_ADDRESS, vga.REG_CURSOR_START,
vga.PORT_DATA, vga.CURSOR_SCANLINE_START,
vga.PORT_ADDRESS, vga.REG_CURSOR_END,
vga.PORT_DATA, vga.CURSOR_SCANLINE_END);
vga.setCursorShape(vga.CursorShape.BLOCK);
}
test "init all" {
arch.initTest();
defer arch.freeTest();
// Mocking out the arch.outb calls for setting the cursor max scan line and the shape to block:
// This will also check that the scan line variables were set properly as these are using in
// the arch.outb call for setting the cursor shape.
arch.addTestParams("outb",
vga.PORT_ADDRESS, vga.REG_MAXIMUM_SCAN_LINE,
vga.PORT_DATA, vga.CURSOR_SCANLINE_END,
vga.PORT_ADDRESS, vga.REG_CURSOR_START,
vga.PORT_DATA, vga.CURSOR_SCANLINE_MIDDLE,
vga.PORT_ADDRESS, vga.REG_CURSOR_END,
vga.PORT_DATA, vga.CURSOR_SCANLINE_END);
vga.init();
}

View file

@ -0,0 +1,4 @@
test "all" {
_ = @import("kernel/test_vga.zig");
_ = @import("kernel/test_tty.zig");
}