Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  clientid_textfile.rs

  Sprache: Rust
 

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

mod common;
use crate::common::*;

use std::fs;
use std::path::Path;

use rkv::Rkv;
use rkv::StoreOptions;
use uuid::Uuid;

use glean_core::metrics::*;
use glean_core::CommonMetricData;
use glean_core::Lifetime;

fn clientid_metric() -> UuidMetric {
    UuidMetric::new(CommonMetricData {
        name: "client_id".into(),
        category: "".into(),
        send_in_pings: vec!["glean_client_info".into()],
        lifetime: Lifetime::User,
        disabled: false,
        dynamic_label: None,
    })
}

fn clientid_from_file(data_path: &Path) -> Option<Uuid> {
    let path = data_path.join("client_id.txt");
    let uuid_str = fs::read_to_string(path).ok()?;
    Uuid::parse_str(uuid_str.trim_end()).ok()
}

fn write_clientid_to_file(data_path: &Path, uuid: &Uuid) -> Option<()> {
    let mut buffer = Uuid::encode_buffer();
    let uuid_str = uuid.hyphenated().encode_lower(&mut buffer);
    write_clientid_to_file_str(data_path, uuid_str)
}

fn write_clientid_to_file_str(data_path: &Path, s: &str) -> Option<()> {
    let path = data_path.join("client_id.txt");
    fs::write(path, s.as_bytes()).ok()?;
    Some(())
}

#[test]
fn writes_clientid_file() {
    let (glean, temp) = new_glean(None);

    let db_client_id = clientid_metric().get_value(&glean, None).unwrap();
    let file_client_id = clientid_from_file(temp.path()).unwrap();

    assert_eq!(file_client_id, db_client_id);

    glean.submit_ping_by_name("health", Some("pre_init"));
    let mut pending = get_queued_pings(temp.path()).unwrap();
    assert_eq!(1, pending.len());
    let payload = pending.pop().unwrap().1;

    let state = &payload["metrics"]["string"]["glean.health.exception_state"];
    assert_eq!(&serde_json::Value::Null, state);
}

#[test]
fn reused_clientid_from_file() {
    let temp = tempfile::tempdir().unwrap();
    let new_uuid = Uuid::new_v4();

    write_clientid_to_file(temp.path(), &new_uuid).unwrap();

    let (glean, temp) = new_glean(Some(temp));
    let db_client_id = clientid_metric().get_value(&glean, None).unwrap();
    assert_eq!(new_uuid, db_client_id);

    glean.submit_ping_by_name("health", Some("pre_init"));
    let mut pending = get_queued_pings(temp.path()).unwrap();
    assert_eq!(1, pending.len());
    let payload = pending.pop().unwrap().1;

    let state = payload["metrics"]["string"]["glean.health.exception_state"].as_str();
    assert_eq!(Some("empty-db"), state);
}

#[test]
fn restores_clientid_file_from_db() {
    let (db_client_id, temp) = {
        // Ensure we initialize once to get a client_id
        let (glean, temp) = new_glean(None);
        let db_client_id = clientid_metric().get_value(&glean, None).unwrap();
        drop(glean);

        (db_client_id, temp)
    };

    // Removing the file. `Glean::new` should restore it
    fs::remove_file(temp.path().join("client_id.txt")).unwrap();

    let (glean, temp) = new_glean(Some(temp));
    let file_client_id = clientid_from_file(temp.path()).unwrap();
    assert_eq!(file_client_id, db_client_id);

    let db_client_id2 = clientid_metric().get_value(&glean, None).unwrap();
    assert_eq!(db_client_id, db_client_id2);

    glean.submit_ping_by_name("health", Some("pre_init"));
    let mut pending = get_queued_pings(temp.path()).unwrap();
    assert_eq!(1, pending.len());
    let payload = pending.pop().unwrap().1;

    let state = payload["metrics"]["string"]["glean.health.exception_state"].as_str();
    assert_eq!(None, state);
}

#[test]
fn clientid_regen_issue_with_existing_db() {
    let (file_client_id, temp) = {
        // Ensure we initialize once to get a client_id
        let (glean, temp) = new_glean(None);
        let file_client_id = clientid_from_file(temp.path()).unwrap();
        drop(glean);

        (file_client_id, temp)
    };

    // We modify the database and ONLY clear out the client id.
    {
        let path = temp.path().join("db");
        let rkv = Rkv::new::<rkv::backend::SafeMode>(&path).unwrap();
        let user_store = rkv.open_single("user", StoreOptions::create()).unwrap();

        // We know this.
        let client_id_key = "glean_client_info#client_id";

        let mut writer = rkv.write().unwrap();
        user_store.delete(&mut writer, client_id_key).unwrap();
        writer.commit().unwrap();
    }

    let (glean, temp) = new_glean(Some(temp));

    let db_client_id = clientid_metric().get_value(&glean, None).unwrap();
    assert_eq!(file_client_id, db_client_id);

    glean.submit_ping_by_name("health", Some("pre_init"));
    let mut pending = get_queued_pings(temp.path()).unwrap();
    assert_eq!(1, pending.len());
    let payload = pending.pop().unwrap().1;

    let state = payload["metrics"]["string"]["glean.health.exception_state"].as_str();
    assert_eq!(Some("regen-db"), state);
}

#[test]
fn db_client_id_prefered_over_file_client_id() {
    let (db_client_id, temp) = {
        // Ensure we initialize once to get a client_id
        let (glean, temp) = new_glean(None);
        let db_client_id = clientid_metric().get_value(&glean, None).unwrap();
        drop(glean);

        (db_client_id, temp)
    };

    // We modify the client id file
    let new_uuid = Uuid::new_v4();
    write_clientid_to_file(temp.path(), &new_uuid).unwrap();

    let (glean, temp) = new_glean(Some(temp));
    let db_client_id2 = clientid_metric().get_value(&glean, None).unwrap();
    assert_eq!(db_client_id, db_client_id2);

    let file_client_id = clientid_from_file(temp.path()).unwrap();
    assert_eq!(file_client_id, db_client_id);

    glean.submit_ping_by_name("health", Some("pre_init"));
    let mut pending = get_queued_pings(temp.path()).unwrap();
    assert_eq!(1, pending.len());
    let payload = pending.pop().unwrap().1;

    let state = payload["metrics"]["string"]["glean.health.exception_state"].as_str();
    assert_eq!(Some("client-id-mismatch"), state);

    let exception_uuid = &payload["metrics"]["uuid"]["glean.health.recovered_client_id"].as_str();
    let mut buffer = Uuid::encode_buffer();
    let expected_uuid = &*new_uuid.hyphenated().encode_lower(&mut buffer);
    assert_eq!(&Some(expected_uuid), exception_uuid);
}

#[test]
fn c0ffee_in_db_gets_overwritten_by_stored_client_id() {
    let (file_client_id, temp) = {
        // Ensure we initialize once to get a client_id
        let (glean, temp) = new_glean(None);
        let file_client_id = clientid_from_file(temp.path()).unwrap();
        drop(glean);

        (file_client_id, temp)
    };

    // We modify the database and ONLY set the client id to c0ffee.
    {
        let path = temp.path().join("db");
        let rkv = Rkv::new::<rkv::backend::SafeMode>(&path).unwrap();
        let user_store = rkv.open_single("user", StoreOptions::create()).unwrap();

        // We know this.
        let client_id_key = "glean_client_info#client_id";

        let mut writer = rkv.write().unwrap();
        let encoded = bincode::serialize(&Metric::Uuid(String::from(
            "c0ffeec0-ffee-c0ff-eec0-ffeec0ffeec0",
        )))
        .unwrap();
        let known_client_id = rkv::Value::Blob(&encoded);
        user_store
            .put(&mut writer, client_id_key, &known_client_id)
            .unwrap();
        writer.commit().unwrap();
    }

    let (glean, temp) = new_glean(Some(temp));

    let db_client_id = clientid_metric().get_value(&glean, None).unwrap();
    assert_eq!(file_client_id, db_client_id);

    glean.submit_ping_by_name("health", Some("pre_init"));
    let mut pending = get_queued_pings(temp.path()).unwrap();
    assert_eq!(1, pending.len());
    let payload = pending.pop().unwrap().1;

    let state = payload["metrics"]["string"]["glean.health.exception_state"].as_str();
    assert_eq!(Some("c0ffee-in-db"), state);

    let mut buffer = Uuid::encode_buffer();
    let file_client_id = &*file_client_id.hyphenated().encode_lower(&mut buffer);

    let exception_uuid = &payload["metrics"]["uuid"]["glean.health.recovered_client_id"].as_str();
    assert_eq!(&Some(file_client_id), exception_uuid);

    let exception_uuid = &payload["client_info"]["client_id"].as_str();
    assert_eq!(&Some(file_client_id), exception_uuid);
    // We ensure we don't see the c0ffee ID either.
    let client_id = payload["client_info"]["client_id"].as_str().unwrap();
    assert_ne!("c0ffeec0-ffee-c0ff-eec0-ffeec0ffeec0", client_id);
}

#[test]
fn clientid_file_gets_deleted() {
    let (mut glean, temp) = new_glean(None);

    let path = temp.path().join("client_id.txt");
    assert!(path.exists());

    let file_client_id = clientid_from_file(temp.path());
    assert!(file_client_id.is_some());

    glean.set_upload_enabled(false);
    assert!(!path.exists());
}

#[test]
fn clientid_file_casing_doesnt_matter() {
    let (file_client_id, temp) = {
        // Ensure we initialize once to get a client_id
        let (glean, temp) = new_glean(None);
        let file_client_id = clientid_from_file(temp.path()).unwrap();
        drop(glean);

        (file_client_id, temp)
    };

    {
        let mut buffer = Uuid::encode_buffer();
        let uuid_str = file_client_id.hyphenated().encode_lower(&mut buffer);
        write_clientid_to_file_str(temp.path(), &uuid_str.to_uppercase());
    }

    let (glean, temp) = new_glean(Some(temp));

    let db_client_id = clientid_metric().get_value(&glean, None).unwrap();
    assert_eq!(file_client_id, db_client_id);

    glean.submit_ping_by_name("health", Some("pre_init"));
    let mut pending = get_queued_pings(temp.path()).unwrap();
    assert_eq!(1, pending.len());
    let payload = pending.pop().unwrap().1;

    let state = &payload["metrics"]["string"]["glean.health.exception_state"];
    assert_eq!(&serde_json::Value::Null, state);
}

#[test]
fn invalid_client_id_file_doesnt_crash() {
    let (glean, mut temp) = new_glean(None);
    drop(glean);

    let test_values = &[
        "",
        "abc",
        "e9f87afa-48b6-489e-841a-401522aaf1f"// one byte short
        "e9f87afa-48b6-489e-841a-401522aaf1ff\n"// explicit newline!
        "k9f87afa-48b6-489e-841a-401522aaf1ff"// k is not a valid char
        "\0\0\0",
    ];

    for value in test_values {
        write_clientid_to_file_str(temp.path(), value);

        let (glean, new_temp) = new_glean(Some(temp));
        drop(glean);
        temp = new_temp;
    }

    // Now testing with a directory in place.
    {
        let p = temp.path().join("client_id.txt");
        fs::remove_file(&p).unwrap();
        fs::create_dir_all(&p).unwrap();

        let (glean, new_temp) = new_glean(Some(temp));
        drop(glean);
        temp = new_temp;
    }

    drop(temp);
}

#[test]
fn single_deletion_request_ping_after_externally_disabling() {
    let (client_id, temp) = {
        let (glean, temp) = new_glean(None);
        let client_id = clientid_metric().get_value(&glean, None).unwrap();

        drop(glean);
        (client_id, temp)
    };

    let (_glean, temp) = new_glean_with_upload(Some(temp), false);

    let mut pings = get_deletion_pings(temp.path()).unwrap();

    // We should have only 1 deletion-request ping pending!
    assert_eq!(1, pings.len());

    let payload = pings.pop().unwrap().1;
    let uuid = &payload["client_info"]["client_id"].as_str();
    assert_eq!(&Some(&*client_id.to_string()), uuid);
}

#[test]
fn extra_deletion_request_ping_is_sent() {
    let (client_id, temp) = {
        let (mut glean, temp) = new_glean(None);
        let client_id = clientid_metric().get_value(&glean, None).unwrap();

        // Turning off upload removes the `client_id.txt` and deletes the database.
        // This also triggers a deletion-request ping
        glean.set_upload_enabled(false);

        let pings = get_deletion_pings(temp.path()).unwrap();
        assert_eq!(1, pings.len());

        (client_id, temp)
    };

    // We restore the `client_id.txt` file as if previous removal failed.
    write_clientid_to_file(temp.path(), &client_id).unwrap();

    let (_glean, temp) = new_glean_with_upload(Some(temp), false);

    let mut pings = get_deletion_pings(temp.path()).unwrap();

    // We now have 2 deletion-request pings pending!
    assert_eq!(2, pings.len());

    // Test both pings. They should be equivalent!
    let payload = pings.pop().unwrap().1;
    let uuid = &payload["client_info"]["client_id"].as_str();
    assert_eq!(&Some(&*client_id.to_string()), uuid);

    let payload = pings.pop().unwrap().1;
    let uuid = &payload["client_info"]["client_id"].as_str();
    assert_eq!(&Some(&*client_id.to_string()), uuid);
}

mod read_errors {
    use super::*;

    #[test]
    fn parse_error_is_reported() {
        let (glean, temp) = new_glean(None);
        drop(glean);

        write_clientid_to_file_str(temp.path(), "abc");

        let (glean, temp) = new_glean(Some(temp));

        glean.submit_ping_by_name("health", Some("pre_init"));
        let mut pending = get_queued_pings(temp.path()).unwrap();
        assert_eq!(1, pending.len());
        let payload = pending.pop().unwrap().1;

        let state = &payload["metrics"]["string"]["glean.health.exception_state"];
        assert_eq!(&serde_json::Value::Null, state);

        let state = payload["metrics"]["labeled_counter"]["glean.health.file_read_error"]["parse"]
            .as_i64()
            .unwrap();
        assert_eq!(1, state);
    }

    #[test]
    fn io_error_is_reported() {
        let (glean, temp) = new_glean(None);
        drop(glean);

        let p = temp.path().join("client_id.txt");
        fs::remove_file(&p).unwrap();
        fs::create_dir_all(&p).unwrap();

        let (glean, temp) = new_glean(Some(temp));

        glean.submit_ping_by_name("health", Some("pre_init"));
        let mut pending = get_queued_pings(temp.path()).unwrap();
        assert_eq!(1, pending.len());
        let payload = pending.pop().unwrap().1;

        let state = &payload["metrics"]["string"]["glean.health.exception_state"];
        assert_eq!(&serde_json::Value::Null, state);

        let state = payload["metrics"]["labeled_counter"]["glean.health.file_read_error"]["io"]
            .as_i64()
            .unwrap();
        assert_eq!(1, state);
    }

    #[cfg(unix)]
    #[test]
    fn permission_error_is_reported() {
        use std::os::unix::fs::PermissionsExt;

        let (glean, temp) = new_glean(None);
        drop(glean);

        let p = temp.path().join("client_id.txt");
        // Remove write permissions on the file.
        let attr = fs::metadata(&p).unwrap();
        let original_permissions = attr.permissions();
        let mut permissions = original_permissions.clone();
        // No permissions at all, no read, no write, for anyone.
        permissions.set_mode(0o0);
        fs::set_permissions(&p, permissions).unwrap();

        let (glean, temp) = new_glean(Some(temp));

        glean.submit_ping_by_name("health", Some("pre_init"));
        let mut pending = get_queued_pings(temp.path()).unwrap();
        assert_eq!(1, pending.len());
        let payload = pending.pop().unwrap().1;

        let state = &payload["metrics"]["string"]["glean.health.exception_state"];
        assert_eq!(&serde_json::Value::Null, state);

        let state = payload["metrics"]["labeled_counter"]["glean.health.file_read_error"]
            ["permission-denied"]
            .as_i64()
            .unwrap();
        assert_eq!(1, state);
    }

    #[test]
    fn c0ffee_is_reported() {
        let (glean, temp) = new_glean(None);
        drop(glean);

        write_clientid_to_file_str(temp.path(), "c0ffeec0-ffee-c0ff-eec0-ffeec0ffeec0");

        let (glean, temp) = new_glean(Some(temp));

        glean.submit_ping_by_name("health", Some("pre_init"));
        let mut pending = get_queued_pings(temp.path()).unwrap();
        assert_eq!(1, pending.len());
        let payload = pending.pop().unwrap().1;

        let state = &payload["metrics"]["string"]["glean.health.exception_state"];
        assert_eq!(&serde_json::Value::Null, state);

        let state = payload["metrics"]["labeled_counter"]["glean.health.file_read_error"]
            ["c0ffee-in-file"]
            .as_i64()
            .unwrap();
        assert_eq!(1, state);
    }

    #[test]
    fn file_not_found_is_reported() {
        let (glean, temp) = new_glean(None);

        glean.submit_ping_by_name("health", Some("pre_init"));
        let mut pending = get_queued_pings(temp.path()).unwrap();
        assert_eq!(1, pending.len());
        let payload = pending.pop().unwrap().1;

        let state = &payload["metrics"]["string"]["glean.health.exception_state"];
        assert_eq!(&serde_json::Value::Null, state);

        let state = payload["metrics"]["labeled_counter"]["glean.health.file_read_error"]
            ["file-not-found"]
            .as_i64()
            .unwrap();
        assert_eq!(1, state);
    }
}

mod write_errors {
    use super::*;

    #[cfg(unix)]
    #[test]
    fn permission_error_is_reported() {
        let (glean, temp) = new_glean(None);
        drop(glean);

        // Force empty file, so that it will be written again later.
        write_clientid_to_file_str(temp.path(), "");

        let p = temp.path().join("client_id.txt");
        // Remove write permissions on the file.
        let attr = fs::metadata(&p).unwrap();
        let original_permissions = attr.permissions();
        let mut permissions = original_permissions.clone();
        // No permissions at all, no read, no write, for anyone.
        permissions.set_readonly(true);
        fs::set_permissions(&p, permissions).unwrap();

        let (glean, temp) = new_glean(Some(temp));

        glean.submit_ping_by_name("health", Some("pre_init"));
        let mut pending = get_queued_pings(temp.path()).unwrap();
        assert_eq!(1, pending.len());
        let payload = pending.pop().unwrap().1;

        let state = &payload["metrics"]["string"]["glean.health.exception_state"];
        assert_eq!(&serde_json::Value::Null, state);

        let state = payload["metrics"]["labeled_counter"]["glean.health.file_read_error"]["parse"]
            .as_i64()
            .unwrap();
        assert_eq!(1, state);

        let state = payload["metrics"]["labeled_counter"]["glean.health.file_write_error"]
            ["permission-denied"]
            .as_i64()
            .unwrap();
        assert_eq!(1, state);
    }

    #[cfg(unix)]
    #[test]
    fn permission_error_on_later_regeneration() {
        let (mut glean, temp) = new_glean(None);

        glean.submit_ping_by_name("health", Some("pre_init"));
        let pending = get_queued_pings(temp.path()).unwrap();
        assert_eq!(1, pending.len());

        glean.set_upload_enabled(false);

        let path = temp.path().join("client_id.txt");
        assert!(!path.exists());

        // Force empty file, so that it will be written again later.
        write_clientid_to_file_str(temp.path(), "");

        let p = temp.path().join("client_id.txt");
        // Remove write permissions on the file.
        let attr = fs::metadata(&p).unwrap();
        let original_permissions = attr.permissions();
        let mut permissions = original_permissions.clone();
        // No permissions at all, no read, no write, for anyone.
        permissions.set_readonly(true);
        fs::set_permissions(&p, permissions).unwrap();

        glean.set_upload_enabled(true);

        glean.submit_ping_by_name("health", Some("pre_init"));
        let mut pending = get_queued_pings(temp.path()).unwrap();
        assert_eq!(1, pending.len());
        let payload = pending.pop().unwrap().1;

        let state = &payload["metrics"]["string"]["glean.health.exception_state"];
        assert_eq!(&serde_json::Value::Null, state);

        let state = &payload["metrics"]["labeled_counter"]["glean.health.file_read_error"]["parse"];
        assert_eq!(&serde_json::Value::Null, state);

        let state = payload["metrics"]["labeled_counter"]["glean.health.file_write_error"]
            ["permission-denied"]
            .as_i64()
            .unwrap();
        assert_eq!(1, state);
    }
}

Messung V0.5 in Prozent
C=95 H=100 G=97

¤ Dauer der Verarbeitung: 0.18 Sekunden  (vorverarbeitet am  2026-08-25) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Statistik
#Sources=434850
#Domains=661743