laburnum 1.17.2

An LSP framework for building language servers and compilers, powered by an incremental query tree with content-addressed storage, task-based dataflow, and parallel queries.
Documentation
// Copyright Two Neutron Stars Incorporated and contributors
// SPDX-License-Identifier: BlueOak-1.0.0

//! `pending_deps` re-dispatch (GLD-0035): a watcher that records a dependency
//! it read while absent is re-run when a matching key later lands. These tests
//! exercise the scheduler registry directly: `register_pending_redispatch`
//! stores a one-shot thunk keyed by `(partition, condition)`, and
//! `on_new_chunk` fires the thunks whose condition matches a committed key.

use {
  super::common::{TestPartition, TestRecordData, TestSortKey, test_scheduler},
  crate::{
    Ident,
    database::{PartitionKey, chunk::RecordWriter, query::SortKeyCondition},
    source::cache::reporter::SourceCacheReader,
  },
  std::sync::{
    Arc,
    atomic::{AtomicUsize, Ordering},
  },
};

/// Commit a single record at `sort_key` to `TestPartition` and drive the
/// scheduler's post-commit dispatch (`on_new_chunk`), exactly as `poll_once`
/// does after a task commits.
fn commit(
  scheduler: &Arc<
    crate::scheduler::Scheduler<
      super::common::TestPartitions,
      crate::server::LaburnumLanguageServer,
    >,
  >,
  sort_key: &str,
) {
  // Distinct content per key: `inserted_keys` only reports keys whose content
  // is new to the content-addressed store (the early-cutoff), so two keys with
  // identical content would dedupe and the second would not be reported.
  let mut writer = RecordWriter::new(Ident::new("writer"));
  writer.insert::<TestPartition>(
    sort_key.to_string(),
    TestRecordData::new(format!("v-{sort_key}"), 0),
  );
  let result = scheduler
    .db
    .commit_chunk(writer.build(), &SourceCacheReader::new_empty_for_test());
  scheduler.on_new_chunk(Ident::new("writer"), result);
}

#[test]
fn redispatch_fires_on_matching_insert() {
  let (scheduler, _conn) = test_scheduler();

  let count = Arc::new(AtomicUsize::new(0));
  let c = count.clone();
  scheduler.register_pending_redispatch(
    TestPartition::KEY,
    SortKeyCondition::Exact(TestSortKey::Test("sk1".to_string())),
    Box::new(move || {
      c.fetch_add(1, Ordering::SeqCst);
    }),
  );

  commit(&scheduler, "sk1");

  assert_eq!(
    count.load(Ordering::SeqCst),
    1,
    "a matching insert fires the re-dispatch"
  );
}

#[test]
fn redispatch_survives_non_matching_insert() {
  let (scheduler, _conn) = test_scheduler();

  let count = Arc::new(AtomicUsize::new(0));
  let c = count.clone();
  scheduler.register_pending_redispatch(
    TestPartition::KEY,
    SortKeyCondition::Exact(TestSortKey::Test("sk1".to_string())),
    Box::new(move || {
      c.fetch_add(1, Ordering::SeqCst);
    }),
  );

  commit(&scheduler, "sk2");
  assert_eq!(
    count.load(Ordering::SeqCst),
    0,
    "a non-matching insert must not fire the re-dispatch"
  );

  commit(&scheduler, "sk1");
  assert_eq!(
    count.load(Ordering::SeqCst),
    1,
    "the surviving registration fires when a matching key later lands"
  );
}

#[test]
fn redispatch_is_one_shot() {
  let (scheduler, _conn) = test_scheduler();

  let count = Arc::new(AtomicUsize::new(0));
  let c = count.clone();
  scheduler.register_pending_redispatch(
    TestPartition::KEY,
    SortKeyCondition::BeginsWith(TestSortKey::Test("sk".to_string())),
    Box::new(move || {
      c.fetch_add(1, Ordering::SeqCst);
    }),
  );

  commit(&scheduler, "sk1");
  commit(&scheduler, "sk2");

  assert_eq!(
    count.load(Ordering::SeqCst),
    1,
    "a fired re-dispatch is removed; a second matching commit does not re-fire it"
  );
}