Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/bench.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
- uses: actions/checkout@v7.0.1
- uses: dtolnay/rust-toolchain@v1
with:
toolchain: v1
toolchain: stable
- uses: Swatinem/rust-cache@v2.9.2

- name: Install system dependencies
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
- uses: actions/checkout@v7.0.1
- uses: dtolnay/rust-toolchain@v1
with:
toolchain: v1
toolchain: stable
targets: x86_64-unknown-linux-gnu
- uses: Swatinem/rust-cache@v2.9.2

Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- uses: actions/checkout@v7.0.1
- uses: dtolnay/rust-toolchain@v1
with:
toolchain: v1
toolchain: stable
- uses: Swatinem/rust-cache@v2.9.2

- name: Install cargo-audit
Expand All @@ -40,6 +40,7 @@ jobs:
- uses: actions/checkout@v7.0.1
- uses: dtolnay/rust-toolchain@v1
with:
toolchain: stable
components: clippy
- uses: Swatinem/rust-cache@v2.9.2

Expand Down
22 changes: 13 additions & 9 deletions crates/presswerk-core/benches/core_bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
// Benchmarks for presswerk-core critical operations.
// Run with: cargo bench -p presswerk-core

use criterion::{Criterion, black_box, criterion_group, criterion_main};
use std::hint::black_box;

use criterion::{Criterion, criterion_group, criterion_main};
use presswerk_core::{AppConfig, DocumentType, JobSource, PrintJob, PrintSettings};

fn bench_config_creation(c: &mut Criterion) {
Expand Down Expand Up @@ -39,7 +41,7 @@ fn bench_config_serialization(c: &mut Criterion) {

c.bench_function("config_deserialize_json", |b| {
b.iter(|| {
let _config: AppConfig = serde_json::from_str(&black_box(&json)).expect("deserialize");
let _config: AppConfig = serde_json::from_str(black_box(&json)).expect("deserialize");
})
});
}
Expand Down Expand Up @@ -98,7 +100,7 @@ fn bench_job_serialization(c: &mut Criterion) {

c.bench_function("job_deserialize_json", |b| {
b.iter(|| {
let _job: PrintJob = serde_json::from_str(&black_box(&json)).expect("deserialize");
let _job: PrintJob = serde_json::from_str(black_box(&json)).expect("deserialize");
})
});
}
Expand All @@ -112,10 +114,12 @@ fn bench_print_settings(c: &mut Criterion) {

c.bench_function("settings_custom", |b| {
b.iter(|| {
let mut _settings = PrintSettings::default();
_settings.copies = black_box(5);
_settings.color = black_box(false);
_settings.scale_to_fit = black_box(false);
let _settings = PrintSettings {
copies: black_box(5),
color: black_box(false),
scale_to_fit: black_box(false),
..Default::default()
};
})
});

Expand All @@ -132,7 +136,7 @@ fn bench_print_settings(c: &mut Criterion) {
c.bench_function("settings_deserialize_json", |b| {
b.iter(|| {
let _settings: PrintSettings =
serde_json::from_str(&black_box(&json)).expect("deserialize");
serde_json::from_str(black_box(&json)).expect("deserialize");
})
});
}
Expand Down Expand Up @@ -183,7 +187,7 @@ fn bench_job_id_operations(c: &mut Criterion) {

c.bench_function("job_id_clone", |b| {
b.iter(|| {
let _id = black_box(&id).clone();
let _id = *black_box(&id);
})
});
}
Expand Down
19 changes: 11 additions & 8 deletions crates/presswerk-core/tests/aspect_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,14 +124,14 @@ fn aspect_invalid_dpi_detection() {

for dpi in valid_dpis {
assert!(
dpi >= MIN_DPI && dpi <= MAX_DPI,
(MIN_DPI..=MAX_DPI).contains(&dpi),
"DPI {} should be valid",
dpi
);
}

for dpi in invalid_dpis {
let is_invalid = dpi < MIN_DPI || dpi > MAX_DPI;
let is_invalid = !(MIN_DPI..=MAX_DPI).contains(&dpi);
assert!(is_invalid, "DPI {} should be detected as invalid", dpi);
}
}
Expand Down Expand Up @@ -295,10 +295,12 @@ fn aspect_bytes_progress_invariant() {

#[test]
fn aspect_encryption_setting_consistency() {
let mut config = AppConfig::default();
let mut config = AppConfig {
encryption_enabled: false,
..Default::default()
};

// If encryption is disabled, audit should still be optional
config.encryption_enabled = false;
assert!(!config.encryption_enabled);
// audit can be true or false independently

Expand Down Expand Up @@ -334,10 +336,11 @@ fn aspect_tls_requirement_with_network_jobs() {

#[test]
fn aspect_auto_accept_policy_consistency() {
let mut config = AppConfig::default();

// auto_accept_network_jobs decision affects workflow
config.auto_accept_network_jobs = false;
let config = AppConfig {
auto_accept_network_jobs: false,
..Default::default()
};
assert!(!config.auto_accept_network_jobs);
// When false, jobs should be held for review
let job = PrintJob::new(
JobSource::Network {
Expand Down
3 changes: 2 additions & 1 deletion crates/presswerk-core/tests/e2e_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ fn e2e_config_with_custom_settings() {
print_timeout_secs: 120,
query_timeout_secs: 30,
easy_mode: false,
..Default::default()
};

assert_eq!(config.default_paper_size, PaperSize::Letter);
Expand Down Expand Up @@ -112,7 +113,7 @@ fn e2e_document_type_detection() {
];

for (filename, expected) in types {
let ext = filename.split('.').last().unwrap_or("");
let ext = filename.split('.').next_back().unwrap_or("");
let detected = DocumentType::from_extension(ext);
assert_eq!(
detected, expected,
Expand Down
24 changes: 14 additions & 10 deletions crates/presswerk-core/tests/property_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,21 @@ use proptest::prelude::*;
proptest! {
#[test]
fn prop_config_port_in_valid_range(port in 1u16..=65535u16) {
let mut config = AppConfig::default();
config.server_port = port;
let config = AppConfig {
server_port: port,
..Default::default()
};
prop_assert!(config.server_port >= 1);
prop_assert!(config.server_port <= 65535);
}

#[test]
fn prop_config_timeouts_positive(timeout_secs in 1u64..=3600u64) {
let mut config = AppConfig::default();
config.print_timeout_secs = timeout_secs;
config.query_timeout_secs = timeout_secs / 2;
let config = AppConfig {
print_timeout_secs: timeout_secs,
query_timeout_secs: timeout_secs / 2,
..Default::default()
};
prop_assert!(config.print_timeout_secs > 0);
prop_assert!(config.query_timeout_secs >= 0);
prop_assert!(config.print_timeout_secs >= config.query_timeout_secs);
}

Expand Down Expand Up @@ -127,8 +129,10 @@ proptest! {
proptest! {
#[test]
fn prop_print_settings_copies_positive(copies in 1u32..=1000u32) {
let mut settings = PrintSettings::default();
settings.copies = copies;
let settings = PrintSettings {
copies,
..Default::default()
};
prop_assert!(settings.copies > 0);
}

Expand Down Expand Up @@ -176,7 +180,7 @@ proptest! {
};

let enum_val = orientation.ipp_enum_value();
prop_assert!(enum_val >= 3 && enum_val <= 6);
prop_assert!((3..=6).contains(&enum_val));
}
}

Expand Down
4 changes: 3 additions & 1 deletion crates/presswerk-document/benches/document_bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
// Currently benchmarks the perspective correction pipeline on a small synthetic
// test image.

use criterion::{Criterion, black_box, criterion_group, criterion_main};
use std::hint::black_box;

use criterion::{Criterion, criterion_group, criterion_main};
use image::{DynamicImage, GrayImage, Luma};

use presswerk_core::PaperSize;
Expand Down
4 changes: 3 additions & 1 deletion crates/presswerk-print/benches/ipp_bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
// Criterion benchmarks for IPP request parsing, response building, and
// document content hashing in the presswerk-print crate.

use criterion::{Criterion, black_box, criterion_group, criterion_main};
use std::hint::black_box;

use criterion::{Criterion, criterion_group, criterion_main};
use sha2::{Digest, Sha256};

use presswerk_print::ipp_server::{
Expand Down
20 changes: 13 additions & 7 deletions crates/presswerk-print/src/capabilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,8 +289,10 @@ mod tests {
attrs.insert("sides-supported".into(), "one-sided".into());
let caps = PrinterCapabilities::from_attributes(&attrs);

let mut settings = PrintSettings::default();
settings.duplex = DuplexMode::LongEdge;
let settings = PrintSettings {
duplex: DuplexMode::LongEdge,
..Default::default()
};

let (corrected, result) = auto_correct_settings(&settings, &caps);
assert!(!result.valid);
Expand Down Expand Up @@ -318,8 +320,10 @@ mod tests {
attrs.insert("copies-supported".into(), "1-10".into());
let caps = PrinterCapabilities::from_attributes(&attrs);

let mut settings = PrintSettings::default();
settings.copies = 50;
let settings = PrintSettings {
copies: 50,
..Default::default()
};

let (corrected, result) = auto_correct_settings(&settings, &caps);
assert_eq!(corrected.copies, 10);
Expand All @@ -329,9 +333,11 @@ mod tests {
#[test]
fn unknown_caps_allows_everything() {
let caps = PrinterCapabilities::from_attributes(&HashMap::new());
let mut settings = PrintSettings::default();
settings.duplex = DuplexMode::ShortEdge;
settings.copies = 99;
let settings = PrintSettings {
duplex: DuplexMode::ShortEdge,
copies: 99,
..Default::default()
};

let (_, result) = auto_correct_settings(&settings, &caps);
// No corrections when capabilities are unknown
Expand Down
11 changes: 6 additions & 5 deletions crates/presswerk-print/src/discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use std::sync::{Arc, Mutex};
use std::time::Duration;

use chrono::Utc;
use mdns_sd::{ServiceDaemon, ServiceEvent, ServiceInfo};
use mdns_sd::{ResolvedService, ServiceDaemon, ServiceEvent};
use tracing::{debug, info, warn};

use presswerk_core::error::{PresswerkError, Result};
Expand Down Expand Up @@ -210,22 +210,23 @@ impl PrinterDiscovery {
debug!(service_type = %stype, "mDNS search stopped");
break;
}
_ => {}
}
}
})
.expect("failed to spawn mDNS listener thread");
}
}

/// Convert a resolved `ServiceInfo` into a `DiscoveredPrinter`.
/// Convert a resolved `ResolvedService` into a `DiscoveredPrinter`.
///
/// TXT record keys (case-insensitive) commonly found on IPP printers:
/// - `printer-make-and-model` — human-readable make/model string
/// - `printer-location` — physical location
/// - `Color` — "T" or "F"
/// - `Duplex` — "T" or "F"
/// - `rp` — resource path (e.g. "ipp/print")
fn service_info_to_printer(info: &ServiceInfo, tls: bool) -> Result<DiscoveredPrinter> {
fn service_info_to_printer(info: &ResolvedService, tls: bool) -> Result<DiscoveredPrinter> {
let name = info.get_fullname().to_owned();
let port = info.get_port();

Expand All @@ -235,7 +236,7 @@ fn service_info_to_printer(info: &ServiceInfo, tls: bool) -> Result<DiscoveredPr
.iter()
.find(|a| a.is_ipv4())
.or_else(|| info.get_addresses().iter().next())
.copied()
.map(|address| address.to_ip_addr())
.ok_or_else(|| PresswerkError::Discovery(format!("no address for service {name}")))?;

// Build the IPP URI from TXT `rp` key or fall back to "ipp/print".
Expand Down Expand Up @@ -273,7 +274,7 @@ fn service_info_to_printer(info: &ServiceInfo, tls: bool) -> Result<DiscoveredPr
}

/// Read a boolean TXT record value. IPP Everywhere uses "T"/"F".
fn txt_bool(info: &ServiceInfo, key: &str) -> bool {
fn txt_bool(info: &ResolvedService, key: &str) -> bool {
info.get_property_val_str(key)
.map(|v| v.eq_ignore_ascii_case("t") || v.eq_ignore_ascii_case("true"))
.unwrap_or(false)
Expand Down
Loading
Loading