laburnum 1.17.0

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

use {
  super::storage::{
    TestPartition,
    TestPartitions,
    TestRecord,
    TestRecordData,
  },
  crate::{
    Ident,
    database::{
      chunk::RecordWriter,
      PartitionKey,
    },
    scheduler::lanes::DEFAULT_LANE,
  },
  macro_rules_attribute::apply,
  smol_macros::test,
  std::sync::Arc,
};

fn test_scheduler() -> (
  std::sync::Arc<
    crate::scheduler::Scheduler<
      TestPartitions,
      crate::server::LaburnumLanguageServer,
    >,
  >,
  crate::connect::ipc::Connection,
) {
  let (server_conn, client_conn) = crate::connect::ipc::Connection::memory();
  let filesystems = std::sync::Arc::new(parking_lot::RwLock::new(Vec::new()));
  let source_cache =
    std::sync::Arc::new(parking_lot::RwLock::new(crate::SourceCache::new()));
  let scheduler = crate::scheduler::Scheduler::new_with_worker_count(
    server_conn,
    Arc::new(crate::server::LaburnumLanguageServer),
    filesystems,
    source_cache,
    1,
  );
  (scheduler, client_conn)
}

fn partition_key() -> Ident {
  TestPartition::KEY
}

#[apply(test!)]
async fn test_basic_module() {
  let (scheduler, _conn) = test_scheduler();

  let module_name = "main_module".to_string();
  let exports = vec![Ident::new("export1"), Ident::new("export2")];
  let module_id = Ident::new(&module_name);

  let exports_clone = exports.clone();
  let module_name_clone = module_name.clone();

  scheduler.queue(
    move |_ctx| {
      let task_id = module_id;
      let name = module_name_clone.clone();
      let exp = exports_clone.clone();
      async move {
        let mut writer = RecordWriter::new(task_id);
        writer.insert::<TestPartition, _>(name.clone(), TestRecordData::Module {
          exports: exp,
        });
        Some(writer)
      }
    },
    DEFAULT_LANE,
  );

  scheduler.spawn_workers();

  let (tx, rx) = async_channel::unbounded();

  scheduler.queue(
    move |mut ctx| {
      async move {
        let query_client = ctx.query_client();
        let results = query_client
          .get_record(partition_key(), module_name.clone())
          .await;

        let success = if !results.is_empty()
          && results.len() == 1
          && let Some(record) = results.get(&results.records()[0])
          && let TestRecord::Test(TestRecordData::Module { exports: exp }) = record
        {
          exp.len() == 2 && exports.as_slice() == exp
        } else {
          false
        };

        let _ = tx.send(success).await;
        None
      }
    },
    DEFAULT_LANE,
  );

  let result = rx
    .recv()
    .await
    .expect("Verification task should send result");

  assert!(result, "Module should have been written with correct data");
}

#[apply(test!)]
async fn test_module_with_function() {
  let (scheduler, _conn) = test_scheduler();

  let module_name = "math_module".to_string();
  let module_id = Ident::new(&module_name);
  let function_name = "add".to_string();
  let function_id = Ident::new(&function_name);

  let function_name_for_module = function_name.clone();
  let function_name_for_task = function_name.clone();
  let function_name_for_verify = function_name.clone();

  scheduler.queue(
    move |_ctx| {
      let task_id = module_id;
      let name = module_name.clone();
      let fn_name = function_name_for_module.clone();
      async move {
        let mut writer = RecordWriter::new(task_id);
        writer.insert::<TestPartition, _>(name, TestRecordData::Module {
          exports: vec![Ident::new(&fn_name)],
        });
        Some(writer)
      }
    },
    DEFAULT_LANE,
  );

  scheduler.queue(
    move |_ctx| {
      let task_id = function_id;
      let name = function_name_for_task.clone();
      async move {
        let mut writer = RecordWriter::new(task_id);
        writer.insert::<TestPartition, _>(name, TestRecordData::Function {
          params:      vec![Ident::new("a"), Ident::new("b")],
          return_type: "i32".to_string(),
        });
        Some(writer)
      }
    },
    DEFAULT_LANE,
  );

  scheduler.spawn_workers();

  let (tx, rx) = async_channel::unbounded();

  scheduler.queue(
    move |mut ctx| {
      async move {
        let query_client = ctx.query_client();
        let results = query_client
          .get_record(partition_key(), function_name_for_verify.clone())
          .await;

        let success = if !results.is_empty()
          && results.len() == 1
          && let Some(record) = results.get(&results.records()[0])
          && let TestRecord::Test(TestRecordData::Function {
            params,
            return_type,
          }) = record
        {
          params.len() == 2 && return_type == "i32"
        } else {
          false
        };

        let _ = tx.send(success).await;
        None
      }
    },
    DEFAULT_LANE,
  );

  let result = rx
    .recv()
    .await
    .expect("Verification task should send result");

  assert!(
    result,
    "Function should have been written with correct data"
  );
}

#[apply(test!)]
async fn test_struct_record() {
  let (scheduler, _conn) = test_scheduler();

  let struct_name = "Point".to_string();
  let struct_id = Ident::new(&struct_name);
  let fields = vec![
    (Ident::new("x"), "f64".to_string()),
    (Ident::new("y"), "f64".to_string()),
  ];

  let fields_clone = fields.clone();
  let struct_name_clone = struct_name.clone();

  scheduler.queue(
    move |_ctx| {
      let task_id = struct_id;
      let name = struct_name_clone.clone();
      let flds = fields_clone.clone();
      async move {
        let mut writer = RecordWriter::new(task_id);
        writer.insert::<TestPartition, _>(name, TestRecordData::Struct { fields: flds });
        Some(writer)
      }
    },
    DEFAULT_LANE,
  );

  scheduler.spawn_workers();

  let (tx, rx) = async_channel::unbounded();

  scheduler.queue(
    move |mut ctx| {
      async move {
        let query_client = ctx.query_client();
        let results = query_client
          .get_record(partition_key(), struct_name.clone())
          .await;

        let success = if !results.is_empty()
          && results.len() == 1
          && let Some(record) = results.get(&results.records()[0])
          && let TestRecord::Test(TestRecordData::Struct { fields: flds }) = record
        {
          flds.len() == 2 && fields.as_slice() == flds
        } else {
          false
        };

        let _ = tx.send(success).await;
        None
      }
    },
    DEFAULT_LANE,
  );

  let result = rx
    .recv()
    .await
    .expect("Verification task should send result");

  assert!(result, "Struct should have been written with correct data");
}