update jenkinsfile

This commit is contained in:
bQUARKz 2026-04-08 06:56:09 +01:00
parent 87c51ba8fc
commit 88c75e43ae
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
7 changed files with 103 additions and 47 deletions

View File

@ -1,4 +1,4 @@
.PHONY: fmt fmt-check clippy test test-debugger-socket ci .PHONY: fmt fmt-check clippy tes-local test-debugger-socket test ci cobertura
fmt: fmt:
cargo fmt cargo fmt
@ -9,10 +9,21 @@ fmt-check:
clippy: clippy:
cargo clippy --workspace --all-features cargo clippy --workspace --all-features
test: test-local:
cargo test --workspace --all-targets --all-features --no-fail-fast cargo test --workspace --all-targets --all-features --no-fail-fast
test-debugger-socket: test-debugger-socket:
cargo test -p prometeu-host-desktop-winit --lib -- --ignored cargo test -p prometeu-host-desktop-winit --lib -- --ignored
ci: fmt-check clippy test coverage:
cargo llvm-cov --workspace --all-features --html --output-dir target/llvm-cov/html --fail-under-lines 20 --fail-under-functions 20 --fail-under-regions 20
coverage-xml:
cargo llvm-cov report --workspace --all-features --cobertura --output-path target/llvm-cov/cobertura.xml
coverage-json:
cargo llvm-cov report --workspace --all-features --json --summary-only --output-path target/llvm-cov/summary.json
test: fmt-check clippy test-local test-debugger-socket
ci: fmt-check clippy coverage
cobertura: coverage-xml coverage-json

View File

@ -188,7 +188,8 @@ impl Audio {
return AudioOpStatus::BankInvalid; return AudioOpStatus::BankInvalid;
} }
let sample = bank_slot.and_then(|bank| bank.samples.get(sample_id as usize).map(Arc::clone)); let sample =
bank_slot.and_then(|bank| bank.samples.get(sample_id as usize).map(Arc::clone));
if let Some(s) = sample { if let Some(s) = sample {
// println!( // println!(
@ -322,8 +323,7 @@ mod tests {
let sound_banks = Arc::clone(&banks) as Arc<dyn SoundBankPoolAccess>; let sound_banks = Arc::clone(&banks) as Arc<dyn SoundBankPoolAccess>;
let mut audio = Audio::new(sound_banks); let mut audio = Audio::new(sound_banks);
let status = let status = audio.play_sample(sample(), MAX_CHANNELS, 255, 128, 1.0, 0, LoopMode::Off);
audio.play_sample(sample(), MAX_CHANNELS, 255, 128, 1.0, 0, LoopMode::Off);
assert_eq!(status, AudioOpStatus::VoiceInvalid); assert_eq!(status, AudioOpStatus::VoiceInvalid);
assert!(!audio.voices.iter().any(|voice| voice.active)); assert!(!audio.voices.iter().any(|voice| voice.active));

View File

@ -872,16 +872,13 @@ impl Gfx {
let glyph = glyph_for_char(c); let glyph = glyph_for_char(c);
let raw = color.0; let raw = color.0;
let row_start = (-y).max(0).min(5) as usize; let row_start = (-y).clamp(0, 5) as usize;
let row_end = (screen_h - y).max(0).min(5) as usize; let row_end = (screen_h - y).clamp(0, 5) as usize;
let col_start = (-x).max(0).min(3) as usize; let col_start = (-x).clamp(0, 3) as usize;
let col_end = (screen_w - x).max(0).min(3) as usize; let col_end = (screen_w - x).clamp(0, 3) as usize;
for (row_idx, row) in glyph for (row_idx, row) in
.iter() glyph.iter().enumerate().skip(row_start).take(row_end.saturating_sub(row_start))
.enumerate()
.skip(row_start)
.take(row_end.saturating_sub(row_start))
{ {
let py = (y + row_idx as i32) as usize; let py = (y + row_idx as i32) as usize;
let base = py * self.w; let base = py * self.w;

View File

@ -87,9 +87,12 @@ impl AssetsPayloadSource {
pub fn open_slice(&self, offset: u64, size: u64) -> io::Result<AssetsPayloadSlice> { pub fn open_slice(&self, offset: u64, size: u64) -> io::Result<AssetsPayloadSlice> {
match self { match self {
Self::Memory(bytes) => { Self::Memory(bytes) => {
let start = usize::try_from(offset).map_err(|_| invalid_input("asset offset overflow"))?; let start =
let len = usize::try_from(size).map_err(|_| invalid_input("asset size overflow"))?; usize::try_from(offset).map_err(|_| invalid_input("asset offset overflow"))?;
let end = start.checked_add(len).ok_or_else(|| invalid_input("asset range overflow"))?; let len =
usize::try_from(size).map_err(|_| invalid_input("asset size overflow"))?;
let end =
start.checked_add(len).ok_or_else(|| invalid_input("asset range overflow"))?;
if end > bytes.len() { if end > bytes.len() {
return Err(invalid_input("asset range out of bounds")); return Err(invalid_input("asset range out of bounds"));
} }
@ -97,7 +100,9 @@ impl AssetsPayloadSource {
Ok(AssetsPayloadSlice::Memory { bytes: Arc::clone(bytes), start, len }) Ok(AssetsPayloadSlice::Memory { bytes: Arc::clone(bytes), start, len })
} }
Self::File(source) => { Self::File(source) => {
let end = offset.checked_add(size).ok_or_else(|| invalid_input("asset range overflow"))?; let end = offset
.checked_add(size)
.ok_or_else(|| invalid_input("asset range overflow"))?;
if end > source.payload_len { if end > source.payload_len {
return Err(invalid_input("asset range out of bounds")); return Err(invalid_input("asset range out of bounds"));
} }

View File

@ -132,11 +132,9 @@ impl MemcardService {
match self.load_committed(fs, app_id, slot) { match self.load_committed(fs, app_id, slot) {
Ok(Some(committed)) => Self::slice_payload(&committed.payload, offset, max_bytes), Ok(Some(committed)) => Self::slice_payload(&committed.payload, offset, max_bytes),
Ok(None) => MemcardReadResult { Ok(None) => {
status: MemcardStatus::Empty, MemcardReadResult { status: MemcardStatus::Empty, bytes: Vec::new(), bytes_read: 0 }
bytes: Vec::new(), }
bytes_read: 0,
},
Err(status) => MemcardReadResult { status, bytes: Vec::new(), bytes_read: 0 }, Err(status) => MemcardReadResult { status, bytes: Vec::new(), bytes_read: 0 },
} }
} }
@ -224,7 +222,11 @@ impl MemcardService {
fn slice_payload(payload: &[u8], offset: usize, max_bytes: usize) -> MemcardReadResult { fn slice_payload(payload: &[u8], offset: usize, max_bytes: usize) -> MemcardReadResult {
if offset >= payload.len() || max_bytes == 0 { if offset >= payload.len() || max_bytes == 0 {
return MemcardReadResult { status: MemcardStatus::Ok, bytes: Vec::new(), bytes_read: 0 }; return MemcardReadResult {
status: MemcardStatus::Ok,
bytes: Vec::new(),
bytes_read: 0,
};
} }
let end = payload.len().min(offset.saturating_add(max_bytes)); let end = payload.len().min(offset.saturating_add(max_bytes));
let bytes = payload[offset..end].to_vec(); let bytes = payload[offset..end].to_vec();
@ -276,11 +278,13 @@ fn make_save_uuid(app_id: u32, slot: u8) -> [u8; 16] {
let mut out = [0u8; 16]; let mut out = [0u8; 16];
out[0..4].copy_from_slice(&app_id.to_le_bytes()); out[0..4].copy_from_slice(&app_id.to_le_bytes());
out[4] = slot; out[4] = slot;
out[5..13].copy_from_slice(&(std::time::SystemTime::now() out[5..13].copy_from_slice(
&(std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH) .duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos() as u64) .map(|d| d.as_nanos() as u64)
.unwrap_or(0)) .unwrap_or(0))
.to_le_bytes()); .to_le_bytes(),
);
out[13] = 0x50; out[13] = 0x50;
out[14] = 0x4D; out[14] = 0x4D;
out[15] = 0x31; out[15] = 0x31;
@ -312,8 +316,10 @@ fn decode_slot_file(bytes: &[u8]) -> Result<SlotImage, MemcardStatus> {
let mut save_uuid = [0u8; 16]; let mut save_uuid = [0u8; 16];
save_uuid.copy_from_slice(&bytes[5..21]); save_uuid.copy_from_slice(&bytes[5..21]);
let generation = u64::from_le_bytes(bytes[21..29].try_into().map_err(|_| MemcardStatus::Corrupt)?); let generation =
let checksum = u32::from_le_bytes(bytes[29..33].try_into().map_err(|_| MemcardStatus::Corrupt)?); u64::from_le_bytes(bytes[21..29].try_into().map_err(|_| MemcardStatus::Corrupt)?);
let checksum =
u32::from_le_bytes(bytes[29..33].try_into().map_err(|_| MemcardStatus::Corrupt)?);
let payload_size = let payload_size =
u32::from_le_bytes(bytes[33..37].try_into().map_err(|_| MemcardStatus::Corrupt)?) as usize; u32::from_le_bytes(bytes[33..37].try_into().map_err(|_| MemcardStatus::Corrupt)?) as usize;
if payload_size > MEMCARD_SLOT_CAPACITY_BYTES { if payload_size > MEMCARD_SLOT_CAPACITY_BYTES {

View File

@ -197,7 +197,7 @@ impl NativeInterface for VirtualMachineRuntime {
let pan_raw = expect_int(args, 3)?; let pan_raw = expect_int(args, 3)?;
let pitch = expect_number(args, 4, "pitch")?; let pitch = expect_number(args, 4, "pitch")?;
if voice_id_raw < 0 || voice_id_raw >= 16 { if !(0..16).contains(&voice_id_raw) {
ret.push_int(AudioOpStatus::VoiceInvalid as i64); ret.push_int(AudioOpStatus::VoiceInvalid as i64);
return Ok(()); return Ok(());
} }
@ -238,7 +238,7 @@ impl NativeInterface for VirtualMachineRuntime {
_ => prometeu_hal::LoopMode::On, _ => prometeu_hal::LoopMode::On,
}; };
if voice_id_raw < 0 || voice_id_raw >= 16 { if !(0..16).contains(&voice_id_raw) {
ret.push_int(AudioOpStatus::VoiceInvalid as i64); ret.push_int(AudioOpStatus::VoiceInvalid as i64);
return Ok(()); return Ok(());
} }
@ -558,7 +558,7 @@ fn hex_decode(s: &str) -> Result<Vec<u8>, VmFault> {
} }
let bytes = s.as_bytes(); let bytes = s.as_bytes();
if bytes.len() % 2 != 0 { if !bytes.len().is_multiple_of(2) {
return Err(VmFault::Trap(TRAP_TYPE, "payload_hex must have even length".to_string())); return Err(VmFault::Trap(TRAP_TYPE, "payload_hex must have even length".to_string()));
} }
let mut out = Vec::with_capacity(bytes.len() / 2); let mut out = Vec::with_capacity(bytes.len() / 2);

View File

@ -1,20 +1,57 @@
pipeline { pipeline {
agent any agent any
environment {
CARGO_TERM_COLOR = 'always'
}
stages { stages {
stage('CI') { stage('Build') {
steps { steps {
withChecks(name: 'Rust CI', includeStage: true) { withChecks(name: 'Test', includeStage: true) {
sh ''' sh '''
set -e set -eux
which rustc
which cargo
rustc --version
cargo --version
make ci make ci
''' '''
} }
publishHTML(target: [
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'target/llvm-cov/html',
reportFiles: 'index.html',
reportName: 'Rust Coverage HTML',
reportTitles: 'Coverage Report'
])
} }
} // Test
stage('Coverage') {
steps {
sh '''
set -eux
make cobertura
'''
recordCoverage(
tools: [[parser: 'COBERTURA', pattern: 'target/llvm-cov/cobertura.xml']],
sourceCodeRetention: 'LAST_BUILD',
enabledForFailure: true,
failOnError: true,
checksAnnotationScope: 'MODIFIED_LINES',
checksName: 'Rust Coverage',
qualityGates: [
[metric: 'LINE', baseline: 'MODIFIED_LINES', threshold: 20.0],
[metric: 'LINE', baseline: 'PROJECT', threshold: 20.0],
[metric: 'BRANCH', baseline: 'PROJECT', threshold: 20.0]
]
)
}
} // Cobertura
}
}
post {
always {
archiveArtifacts artifacts: 'target/llvm-cov/**', fingerprint: true
} }
} }
} }