Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix mbtiles validation, CI, and logging #903

Merged
merged 2 commits into from
Sep 29, 2023
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion justfile
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ test-int: clean-test install-sqlx

# Run integration tests and save its output as the new expected output
bless: start clean-test
rm -rf tests/temp
cargo test --features bless-tests
tests/test.sh
rm -rf tests/expected
Expand Down Expand Up @@ -216,7 +217,7 @@ git-pre-push: stop start
# Update sqlite database schema.
prepare-sqlite: install-sqlx
mkdir -p martin-mbtiles/.sqlx
cd martin-mbtiles && cargo sqlx prepare --database-url sqlite://$PWD/../tests/fixtures/files/world_cities.mbtiles -- --lib --tests
cd martin-mbtiles && cargo sqlx prepare --database-url sqlite://$PWD/../tests/fixtures/mbtiles/world_cities.mbtiles -- --lib --tests

# Install SQLX cli if not already installed.
[private]
Expand Down
3 changes: 2 additions & 1 deletion martin-mbtiles/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ rust-version.workspace = true
[features]
# TODO: Disable "cli" feature in default builds
default = ["cli", "native-tls"]
cli = ["dep:anyhow", "dep:clap", "dep:tokio"]
cli = ["dep:anyhow", "dep:clap", "dep:env_logger", "dep:tokio"]
# One of the following two must be used
native-tls = ["sqlx/runtime-tokio-native-tls"]
rustls = ["sqlx/runtime-tokio-rustls"]
Expand All @@ -30,6 +30,7 @@ tilejson.workspace = true
# Bin dependencies
anyhow = { workspace = true, optional = true }
clap = { workspace = true, optional = true }
env_logger = { workspace = true, optional = true }
serde_yaml.workspace = true
sqlite-hashes.workspace = true
tokio = { workspace = true, features = ["rt-multi-thread"], optional = true }
Expand Down
33 changes: 21 additions & 12 deletions martin-mbtiles/src/bin/main.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use std::path::{Path, PathBuf};

use clap::{Parser, Subcommand};
use log::{error, LevelFilter};
use martin_mbtiles::{
apply_mbtiles_diff, IntegrityCheckType, MbtResult, Mbtiles, TileCopierOptions,
};
use sqlx::sqlite::SqliteConnectOptions;
use sqlx::{Connection, SqliteConnection};

#[derive(Parser, PartialEq, Eq, Debug)]
#[command(
Expand Down Expand Up @@ -73,9 +72,23 @@ enum Commands {
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let args = Args::parse();
async fn main() {
env_logger::builder()
.filter_level(LevelFilter::Info)
.format_indent(None)
.format_module_path(false)
.format_target(false)
.format_timestamp(None)
.init();

if let Err(err) = main_int().await {
error!("{err}");
std::process::exit(1);
}
}

async fn main_int() -> anyhow::Result<()> {
let args = Args::parse();
match args.command {
Commands::MetaAll { file } => {
meta_print_all(file.as_path()).await?;
Expand Down Expand Up @@ -109,17 +122,15 @@ async fn main() -> anyhow::Result<()> {

async fn meta_print_all(file: &Path) -> anyhow::Result<()> {
let mbt = Mbtiles::new(file)?;
let opt = SqliteConnectOptions::new().filename(file).read_only(true);
let mut conn = SqliteConnection::connect_with(&opt).await?;
let mut conn = mbt.open_with_hashes(true).await?;
let metadata = mbt.get_metadata(&mut conn).await?;
println!("{}", serde_yaml::to_string(&metadata)?);
Ok(())
}

async fn meta_get_value(file: &Path, key: &str) -> MbtResult<()> {
let mbt = Mbtiles::new(file)?;
let opt = SqliteConnectOptions::new().filename(file).read_only(true);
let mut conn = SqliteConnection::connect_with(&opt).await?;
let mut conn = mbt.open_with_hashes(true).await?;
if let Some(s) = mbt.get_metadata_value(&mut conn, key).await? {
println!("{s}");
}
Expand All @@ -128,8 +139,7 @@ async fn meta_get_value(file: &Path, key: &str) -> MbtResult<()> {

async fn meta_set_value(file: &Path, key: &str, value: Option<String>) -> MbtResult<()> {
let mbt = Mbtiles::new(file)?;
let opt = SqliteConnectOptions::new().filename(file);
let mut conn = SqliteConnection::connect_with(&opt).await?;
let mut conn = mbt.open_with_hashes(false).await?;
mbt.set_metadata_value(&mut conn, key, value).await
}

Expand All @@ -139,8 +149,7 @@ async fn validate_mbtiles(
update_agg_tiles_hash: bool,
) -> MbtResult<()> {
let mbt = Mbtiles::new(file)?;
let opt = SqliteConnectOptions::new().filename(file).read_only(true);
let mut conn = SqliteConnection::connect_with(&opt).await?;
let mut conn = mbt.open_with_hashes(!update_agg_tiles_hash).await?;
mbt.check_integrity(&mut conn, check_type).await?;
mbt.check_each_tile_hash(&mut conn).await?;
if update_agg_tiles_hash {
Expand Down
51 changes: 25 additions & 26 deletions martin-mbtiles/src/mbtiles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,9 @@ impl Mbtiles {
where
for<'e> &'e mut T: SqliteExecutor<'e>,
{
let filepath = self.filepath();
if integrity_check == IntegrityCheckType::Off {
info!("Skipping integrity check for {filepath}");
return Ok(());
}

Expand All @@ -452,30 +454,32 @@ impl Mbtiles {

if result.len() > 1
|| result.get(0).ok_or(FailedIntegrityCheck(
self.filepath().to_string(),
filepath.to_string(),
vec!["SQLite could not perform integrity check".to_string()],
))? != "ok"
{
return Err(FailedIntegrityCheck(self.filepath().to_string(), result));
}

info!("{integrity_check:?} integrity check passed for {filepath}");
Ok(())
}

pub async fn check_agg_tiles_hashes<T>(&self, conn: &mut T) -> MbtResult<()>
where
for<'e> &'e mut T: SqliteExecutor<'e>,
{
let filepath = self.filepath();
let Some(stored) = self.get_agg_tiles_hash(&mut *conn).await? else {
return Err(AggHashValueNotFound(self.filepath().to_string()));
return Err(AggHashValueNotFound(filepath.to_string()));
};

let computed = calc_agg_tiles_hash(&mut *conn).await?;
if stored != computed {
let file = self.filepath().to_string();
let file = filepath.to_string();
return Err(AggHashMismatch(computed, stored, file));
}

info!("The agg_tiles_hashes={computed} has been verified for {filepath}");
Ok(())
}

Expand All @@ -486,23 +490,15 @@ impl Mbtiles {
{
let old_hash = self.get_agg_tiles_hash(&mut *conn).await?;
let hash = calc_agg_tiles_hash(&mut *conn).await?;
let path = self.filepath();
if old_hash.as_ref() == Some(&hash) {
info!(
"agg_tiles_hash is already set to the correct value `{hash}` in {}",
self.filepath()
);
info!("agg_tiles_hash is already set to the correct value `{hash}` in {path}");
Ok(())
} else {
if let Some(old_hash) = old_hash {
info!(
"Updating agg_tiles_hash from {old_hash} to {hash} in {}",
self.filepath()
);
info!("Updating agg_tiles_hash from {old_hash} to {hash} in {path}");
} else {
info!(
"Initializing agg_tiles_hash to {hash} in {}",
self.filepath()
);
info!("Creating new metadata value agg_tiles_hash = {hash} in {path}");
}
self.set_metadata_value(&mut *conn, "agg_tiles_hash", Some(hash))
.await
Expand Down Expand Up @@ -550,7 +546,10 @@ impl Mbtiles {
v.get(0),
v.get(1),
))
})
})?;

info!("All tile hashes are valid for {}", self.filepath());
Ok(())
}
}

Expand Down Expand Up @@ -610,15 +609,15 @@ mod tests {

#[actix_rt::test]
async fn mbtiles_meta() {
let filepath = "../tests/fixtures/files/geography-class-jpg.mbtiles";
let filepath = "../tests/fixtures/mbtiles/geography-class-jpg.mbtiles";
let mbt = Mbtiles::new(filepath).unwrap();
assert_eq!(mbt.filepath(), filepath);
assert_eq!(mbt.filename(), "geography-class-jpg");
}

#[actix_rt::test]
async fn metadata_jpeg() {
let (mut conn, mbt) = open("../tests/fixtures/files/geography-class-jpg.mbtiles").await;
let (mut conn, mbt) = open("../tests/fixtures/mbtiles/geography-class-jpg.mbtiles").await;
let metadata = mbt.get_metadata(&mut conn).await.unwrap();
let tj = metadata.tilejson;

Expand All @@ -635,7 +634,7 @@ mod tests {

#[actix_rt::test]
async fn metadata_mvt() {
let (mut conn, mbt) = open("../tests/fixtures/files/world_cities.mbtiles").await;
let (mut conn, mbt) = open("../tests/fixtures/mbtiles/world_cities.mbtiles").await;
let metadata = mbt.get_metadata(&mut conn).await.unwrap();
let tj = metadata.tilejson;

Expand Down Expand Up @@ -666,7 +665,7 @@ mod tests {

#[actix_rt::test]
async fn metadata_get_key() {
let (mut conn, mbt) = open("../tests/fixtures/files/world_cities.mbtiles").await;
let (mut conn, mbt) = open("../tests/fixtures/mbtiles/world_cities.mbtiles").await;

let res = mbt.get_metadata_value(&mut conn, "bounds").await.unwrap();
assert_eq!(res.unwrap(), "-123.123590,-37.818085,174.763027,59.352706");
Expand Down Expand Up @@ -726,15 +725,15 @@ mod tests {

#[actix_rt::test]
async fn detect_type() {
let (mut conn, mbt) = open("../tests/fixtures/files/world_cities.mbtiles").await;
let (mut conn, mbt) = open("../tests/fixtures/mbtiles/world_cities.mbtiles").await;
let res = mbt.detect_type(&mut conn).await.unwrap();
assert_eq!(res, MbtType::Flat);

let (mut conn, mbt) = open("../tests/fixtures/files/zoomed_world_cities.mbtiles").await;
let (mut conn, mbt) = open("../tests/fixtures/mbtiles/zoomed_world_cities.mbtiles").await;
let res = mbt.detect_type(&mut conn).await.unwrap();
assert_eq!(res, MbtType::FlatWithHash);

let (mut conn, mbt) = open("../tests/fixtures/files/geography-class-jpg.mbtiles").await;
let (mut conn, mbt) = open("../tests/fixtures/mbtiles/geography-class-jpg.mbtiles").await;
let res = mbt.detect_type(&mut conn).await.unwrap();
assert_eq!(res, MbtType::Normalized);

Expand All @@ -745,7 +744,7 @@ mod tests {

#[actix_rt::test]
async fn validate_valid_file() {
let (mut conn, mbt) = open("../tests/fixtures/files/zoomed_world_cities.mbtiles").await;
let (mut conn, mbt) = open("../tests/fixtures/mbtiles/zoomed_world_cities.mbtiles").await;

mbt.check_integrity(&mut conn, IntegrityCheckType::Quick)
.await
Expand All @@ -755,7 +754,7 @@ mod tests {
#[actix_rt::test]
async fn validate_invalid_file() {
let (mut conn, mbt) =
open("../tests/fixtures/files/invalid/invalid_zoomed_world_cities.mbtiles").await;
open("../tests/fixtures/files/invalid_zoomed_world_cities.mbtiles").await;
let result = mbt.check_agg_tiles_hashes(&mut conn).await;
assert!(matches!(result, Err(MbtError::AggHashMismatch(..))));
}
Expand Down
Loading
Loading