mirror of
https://github.com/Noratrieb/libuwuc.git
synced 2026-01-14 11:45:05 +01:00
tests
This commit is contained in:
parent
ae189e8b18
commit
043c960708
15 changed files with 206 additions and 23 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
||||||
/target
|
/target
|
||||||
/example-user/target
|
/example-user/target
|
||||||
|
/tests/target
|
||||||
/.vscode
|
/.vscode
|
||||||
8
hello.c
8
hello.c
|
|
@ -1,6 +1,8 @@
|
||||||
#include<stdio.h>
|
#include<stdio.h>
|
||||||
#include<sys/mman.h>
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
PROT_WRITE;
|
int result = printf("Hello, world!\n");
|
||||||
puts("Hello, world!");
|
if (result != 15) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,7 @@ pub unsafe fn mmap(
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore = "uh"]
|
||||||
fn malloc_free() {
|
fn malloc_free() {
|
||||||
unsafe {
|
unsafe {
|
||||||
let x = super::malloc_zeroed(10, 8);
|
let x = super::malloc_zeroed(10, 8);
|
||||||
|
|
|
||||||
|
|
@ -99,15 +99,10 @@ mod tests {
|
||||||
use std::string::ToString;
|
use std::string::ToString;
|
||||||
use std::{ffi::CString, vec::Vec};
|
use std::{ffi::CString, vec::Vec};
|
||||||
|
|
||||||
use crate::utils::SharedThinCstr;
|
use crate::utils::{cstr, SharedThinCstr};
|
||||||
|
|
||||||
use super::EnvP;
|
use super::EnvP;
|
||||||
|
|
||||||
fn cstr(s: &str) -> SharedThinCstr {
|
|
||||||
assert_eq!(s.as_bytes()[s.len() - 1], 0);
|
|
||||||
unsafe { SharedThinCstr::from_ptr(NonNull::new(s.as_ptr() as _).unwrap()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_envp(env: &[&str], f: impl FnOnce(EnvP)) {
|
fn with_envp(env: &[&str], f: impl FnOnce(EnvP)) {
|
||||||
let cstrs = env
|
let cstrs = env
|
||||||
.iter()
|
.iter()
|
||||||
|
|
|
||||||
1
libuwuc/src/fmt/mod.rs
Normal file
1
libuwuc/src/fmt/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
pub mod printf;
|
||||||
52
libuwuc/src/fmt/printf.rs
Normal file
52
libuwuc/src/fmt/printf.rs
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
use core::ffi::VaList;
|
||||||
|
|
||||||
|
use crate::{io::IoWrite, utils::SharedThinCstr};
|
||||||
|
|
||||||
|
pub unsafe fn printf_generic(
|
||||||
|
mut sink: impl IoWrite,
|
||||||
|
format: SharedThinCstr,
|
||||||
|
args: VaList<'_, '_>,
|
||||||
|
) -> Result<(), i32> {
|
||||||
|
let mut chars = format.into_iter();
|
||||||
|
|
||||||
|
while let Some(c) = chars.next() {
|
||||||
|
if c == b'%' {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
|
sink.write_byte(c)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[allow(improper_ctypes_definitions)]
|
||||||
|
mod tests {
|
||||||
|
extern crate std;
|
||||||
|
use std::string::String;
|
||||||
|
use std::vec::Vec;
|
||||||
|
|
||||||
|
use crate::utils::{cstr, SharedThinCstr};
|
||||||
|
|
||||||
|
use super::printf_generic;
|
||||||
|
|
||||||
|
unsafe extern "C" fn test_printf(expected: &str, fmt: SharedThinCstr, mut args: ...) {
|
||||||
|
let mut sink = Vec::new();
|
||||||
|
|
||||||
|
printf_generic(&mut sink, fmt, args.as_va_list()).unwrap();
|
||||||
|
|
||||||
|
let result = String::from_utf8(sink).unwrap();
|
||||||
|
assert_eq!(result, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_format() {
|
||||||
|
unsafe { test_printf("\0", cstr("\0")) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn constant_string() {
|
||||||
|
unsafe { test_printf("hello, world\0", cstr("hello, world\0")) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
pub mod stream;
|
pub mod stream;
|
||||||
|
pub mod traits;
|
||||||
|
|
||||||
|
pub use traits::IoWrite;
|
||||||
|
|
||||||
use core::ffi::c_char;
|
use core::ffi::c_char;
|
||||||
|
|
||||||
|
|
@ -30,13 +33,19 @@ macro_rules! println {
|
||||||
}
|
}
|
||||||
pub use println;
|
pub use println;
|
||||||
|
|
||||||
|
pub unsafe fn write(fd: i32, buf: &[u8]) -> Result<usize, i32> {
|
||||||
|
let result = syscall::syscall!(syscall::SYS_WRITE, fd, buf.as_ptr(), buf.len()) as i64;
|
||||||
|
if result < 0 {
|
||||||
|
Err(result as _)
|
||||||
|
} else {
|
||||||
|
Ok(result as _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub unsafe fn write_all(fd: i32, mut buf: &[u8]) -> Result<(), i64> {
|
pub unsafe fn write_all(fd: i32, mut buf: &[u8]) -> Result<(), i64> {
|
||||||
while !buf.is_empty() {
|
while !buf.is_empty() {
|
||||||
let result = syscall::syscall!(syscall::SYS_WRITE, fd, buf.as_ptr(), buf.len()) as i64;
|
let result = write(fd, buf)?;
|
||||||
if result < 0 {
|
buf = &buf[result..];
|
||||||
return Err(result);
|
|
||||||
}
|
|
||||||
buf = &buf[(result as usize)..];
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use core::ffi::c_int;
|
use core::ffi::c_int;
|
||||||
|
|
||||||
use super::EOF;
|
use super::{IoWrite, EOF};
|
||||||
|
|
||||||
/// A `FILE`.
|
/// A `FILE`.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
@ -18,6 +18,12 @@ impl FileStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IoWrite for &FileStream {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> Result<usize, i32> {
|
||||||
|
unsafe { super::write(self.fd, buf) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn fputc(c: u8, stream: &FileStream) -> i32 {
|
pub fn fputc(c: u8, stream: &FileStream) -> i32 {
|
||||||
match stream.write_byte(c) {
|
match stream.write_byte(c) {
|
||||||
Ok(_) => c as _,
|
Ok(_) => c as _,
|
||||||
|
|
|
||||||
46
libuwuc/src/io/traits.rs
Normal file
46
libuwuc/src/io/traits.rs
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
pub trait IoWrite {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> Result<usize, i32>;
|
||||||
|
|
||||||
|
fn write_all(&mut self, mut buf: &[u8]) -> Result<(), i32> {
|
||||||
|
while !buf.is_empty() {
|
||||||
|
let n = self.write(buf)?;
|
||||||
|
buf = &buf[n..];
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_byte(&mut self, byte: u8) -> Result<(), i32> {
|
||||||
|
self.write_all(&[byte])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: IoWrite> IoWrite for &mut W {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> Result<usize, i32> {
|
||||||
|
W::write(self, buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WriteCounter<W>(pub W, pub usize);
|
||||||
|
|
||||||
|
impl<W: IoWrite> IoWrite for WriteCounter<W> {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> Result<usize, i32> {
|
||||||
|
let n = self.0.write(buf)?;
|
||||||
|
self.1 += n;
|
||||||
|
Ok(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_impls {
|
||||||
|
extern crate std;
|
||||||
|
|
||||||
|
use super::IoWrite;
|
||||||
|
use std::vec::Vec;
|
||||||
|
|
||||||
|
impl IoWrite for Vec<u8> {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> Result<usize, i32> {
|
||||||
|
self.extend(buf);
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
#![feature(c_variadic)]
|
||||||
#![warn(unreachable_pub)]
|
#![warn(unreachable_pub)]
|
||||||
|
|
||||||
#![allow(clippy::missing_safety_doc)]
|
#![allow(clippy::missing_safety_doc)]
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
@ -8,6 +8,7 @@ extern crate std;
|
||||||
|
|
||||||
pub mod alloc;
|
pub mod alloc;
|
||||||
pub mod env;
|
pub mod env;
|
||||||
|
pub mod fmt;
|
||||||
pub mod io;
|
pub mod io;
|
||||||
pub mod mem;
|
pub mod mem;
|
||||||
pub mod start;
|
pub mod start;
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,10 @@ impl SharedThinCstr {
|
||||||
Self(ptr)
|
Self(ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub unsafe fn from_raw(ptr: *const c_char) -> Self {
|
||||||
|
Self(NonNull::new_unchecked(ptr as _))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn from_array<const N: usize>(arr: &[u8; N]) -> Self {
|
pub fn from_array<const N: usize>(arr: &[u8; N]) -> Self {
|
||||||
assert!(arr[N - 1] == 0);
|
assert!(arr[N - 1] == 0);
|
||||||
unsafe { Self(NonNull::new_unchecked(arr as *const u8 as *mut c_char)) }
|
unsafe { Self(NonNull::new_unchecked(arr as *const u8 as *mut c_char)) }
|
||||||
|
|
@ -35,7 +39,7 @@ impl SharedThinCstr {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoIterator for SharedThinCstr {
|
impl IntoIterator for SharedThinCstr {
|
||||||
type Item = c_char;
|
type Item = u8;
|
||||||
|
|
||||||
type IntoIter = CStrIter;
|
type IntoIter = CStrIter;
|
||||||
|
|
||||||
|
|
@ -47,7 +51,7 @@ impl IntoIterator for SharedThinCstr {
|
||||||
pub struct CStrIter(SharedThinCstr);
|
pub struct CStrIter(SharedThinCstr);
|
||||||
|
|
||||||
impl Iterator for CStrIter {
|
impl Iterator for CStrIter {
|
||||||
type Item = c_char;
|
type Item = u8;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
@ -57,7 +61,7 @@ impl Iterator for CStrIter {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.0 = self.0.add(1);
|
self.0 = self.0.add(1);
|
||||||
Some(c)
|
Some(c as u8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -89,6 +93,12 @@ impl PartialEq for SharedThinCstr {
|
||||||
|
|
||||||
impl Eq for SharedThinCstr {}
|
impl Eq for SharedThinCstr {}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn cstr(s: &str) -> SharedThinCstr {
|
||||||
|
assert_eq!(s.as_bytes()[s.len() - 1], 0);
|
||||||
|
unsafe { SharedThinCstr::from_ptr(NonNull::new(s.as_ptr() as _).unwrap()) }
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub(crate) struct SyncPtr<T>(pub(crate) *mut T);
|
pub(crate) struct SyncPtr<T>(pub(crate) *mut T);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
#![feature(c_variadic)]
|
||||||
#![feature(panic_info_message)]
|
#![feature(panic_info_message)]
|
||||||
#![deny(clippy::no_mangle_with_rust_abi)]
|
#![deny(clippy::no_mangle_with_rust_abi)]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,33 @@
|
||||||
use core::ffi::{c_char, c_int};
|
use core::ffi::{c_char, c_int};
|
||||||
|
|
||||||
use libuwuc::io::{stream::FileStream, STDERR, STDIN, STDOUT};
|
use libuwuc::{
|
||||||
|
io::{stream::FileStream, traits::WriteCounter, STDERR, STDIN, STDOUT},
|
||||||
|
utils::SharedThinCstr,
|
||||||
|
};
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn puts(s: *const c_char) -> i32 {
|
pub unsafe extern "C" fn puts(s: *const c_char) -> i32 {
|
||||||
libuwuc::io::puts(s)
|
libuwuc::io::puts(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PRINTF:
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn __printf_chk(_flag: c_int, format: *const c_char, mut args: ...) -> c_int {
|
||||||
|
let mut sink = WriteCounter(stdout, 0);
|
||||||
|
|
||||||
|
let result = libuwuc::fmt::printf::printf_generic(
|
||||||
|
&mut sink,
|
||||||
|
SharedThinCstr::from_raw(format),
|
||||||
|
args.as_va_list(),
|
||||||
|
);
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(()) => sink.1 as _,
|
||||||
|
Err(err) => err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// STREAMS:
|
// STREAMS:
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
|
|
||||||
35
test_c.sh
35
test_c.sh
|
|
@ -1,5 +1,34 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
cargo build
|
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||||
./uwuc-gcc hello.c -o target/hello
|
|
||||||
./target/hello
|
cargo build --manifest-path "$SCRIPT_DIR/Cargo.toml"
|
||||||
|
|
||||||
|
test_dir=$(mktemp -d)
|
||||||
|
|
||||||
|
clean() {
|
||||||
|
rm -r "${test_dir}"
|
||||||
|
}
|
||||||
|
|
||||||
|
for test in tests/c/*; do
|
||||||
|
name=$(basename $test .c)
|
||||||
|
|
||||||
|
"$SCRIPT_DIR/uwuc-gcc" "$test" -o "$test_dir/$name"
|
||||||
|
|
||||||
|
if [ "$?" -ne "0" ]; then
|
||||||
|
echo "error: failed to compile test $test"
|
||||||
|
clean
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
OUTPUT=$("$test_dir/$name")
|
||||||
|
code="$?"
|
||||||
|
if [ "$code" -ne "0" ]; then
|
||||||
|
echo "error: test failed with code $code: $test, running $test_dir/$name"
|
||||||
|
echo "------ output:"
|
||||||
|
echo "$OUTPUT"
|
||||||
|
echo "-----"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
clean
|
||||||
|
|
|
||||||
8
tests/c/simple_printf.c
Normal file
8
tests/c/simple_printf.c
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#include<stdio.h>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
int result = printf("Hello, world!\n");
|
||||||
|
if (result != 14) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue