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 {
  crate::{
    Uri,
    fs::*,
  },
  std::{
    collections::HashMap,
    fs,
  },
  tempfile::TempDir,
};

#[test]
fn test_memory_fs_iter_files_empty() {
  let fs = MemoryFileSystem::new();
  let result = fs.iter_files().unwrap();
  let files: Vec<_> = result.collect();
  assert_eq!(files.len(), 0);
}

#[test]
fn test_memory_fs_iter_files_single_file() {
  let fs = MemoryFileSystem::new();
  let file_uri = fs.uri().join("test.txt").unwrap();
  let content = b"Hello, World!";

  fs.write(&file_uri, content).unwrap();

  let result = fs.iter_files().unwrap();
  let files: Vec<_> = result.collect();

  assert_eq!(files.len(), 1);
  assert_eq!(files[0].0, file_uri);
  assert_eq!(files[0].1, content);
}

#[test]
fn test_memory_fs_iter_files_multiple_files() {
  let fs = MemoryFileSystem::new();

  let files_data: Vec<(&str, &[u8])> = vec![
    ("file1.txt", b"Content 1"),
    ("file2.txt", b"Content 2"),
    ("subdir/file3.txt", b"Content 3"),
  ];

  let mut expected_files = HashMap::new();

  for (path, content) in &files_data {
    let uri = fs.uri().join(path).unwrap();
    fs.write(&uri, content).unwrap();
    expected_files.insert(uri, content.to_vec());
  }

  let result = fs.iter_files().unwrap();
  let actual_files: HashMap<Uri, Vec<u8>> = result.collect();

  assert_eq!(actual_files.len(), expected_files.len());
  for (uri, content) in expected_files {
    assert_eq!(actual_files.get(&uri), Some(&content));
  }
}

#[test]
fn test_memory_fs_iter_files_nested_structure() {
  let fs = MemoryFileSystem::new();

  let files_data: Vec<(&str, &[u8])> = vec![
    ("docs/index.md", b"# Documentation"),
    ("docs/api/v1.md", b"## API v1"),
    ("docs/api/v2.md", b"## API v2"),
    ("docs/api/examples/basic.md", b"Basic example"),
    ("docs/api/examples/advanced.md", b"Advanced example"),
    ("src/main.rs", b"fn main() {}"),
    ("src/lib.rs", b"pub mod test {}"),
  ];

  let mut expected_files = HashMap::new();

  for (path, content) in &files_data {
    let uri = fs.uri().join(path).unwrap();
    fs.write(&uri, content).unwrap();
    expected_files.insert(uri, content.to_vec());
  }

  let result = fs.iter_files().unwrap();
  let actual_files: HashMap<Uri, Vec<u8>> = result.collect();

  assert_eq!(actual_files.len(), expected_files.len());
  for (uri, content) in expected_files {
    assert_eq!(actual_files.get(&uri), Some(&content));
  }
}

#[test]
fn test_memory_fs_iter_files_ignores_directories() {
  let fs = MemoryFileSystem::new();

  // Create some directories and files
  let dir_uri = fs.uri().join("empty_dir/").unwrap();
  fs.dir(&dir_uri).unwrap();

  let file_uri = fs.uri().join("test_file.txt").unwrap();
  let content = b"Test content";
  fs.write(&file_uri, content).unwrap();

  let nested_file_uri = fs.uri().join("nested/file.txt").unwrap();
  let nested_content = b"Nested content";
  fs.write(&nested_file_uri, nested_content).unwrap();

  let result = fs.iter_files().unwrap();
  let files: Vec<_> = result.collect();

  // Should only get files, not directories
  assert_eq!(files.len(), 2);

  let file_map: HashMap<Uri, Vec<u8>> = files.into_iter().collect();
  assert_eq!(file_map.get(&file_uri), Some(&content.to_vec()));
  assert_eq!(
    file_map.get(&nested_file_uri),
    Some(&nested_content.to_vec())
  );
}

#[test]
fn test_physical_fs_iter_files_empty() {
  let temp_dir = TempDir::new().unwrap();
  let fs =
    PhysicalFileSystem::new(Uri::from_file_path(temp_dir.path()).unwrap())
      .unwrap();

  let result = fs.iter_files().unwrap();
  let files: Vec<_> = result.collect();
  assert_eq!(files.len(), 0);
}

#[test]
fn test_physical_fs_iter_files_single_file() {
  let temp_dir = TempDir::new().unwrap();
  let file_path = temp_dir.path().join("test.txt");
  let content = b"Hello, World!";

  fs::write(&file_path, content).unwrap();

  let fs =
    PhysicalFileSystem::new(Uri::from_file_path(temp_dir.path()).unwrap())
      .unwrap();

  let result = fs.iter_files().unwrap();
  let files: Vec<_> = result.collect();

  assert_eq!(files.len(), 1);
  assert_eq!(files[0].1, content);
  // Uri should end with the filename
  assert!(files[0].0.path_str().ends_with("test.txt"));
}

#[test]
fn test_physical_fs_iter_files_multiple_files() {
  let temp_dir = TempDir::new().unwrap();

  let files_data: Vec<(&str, &[u8])> =
    vec![("file1.txt", b"Content 1"), ("file2.txt", b"Content 2")];

  // Create subdirectory
  let subdir = temp_dir.path().join("subdir");
  fs::create_dir(&subdir).unwrap();

  let mut expected_files: HashMap<&str, Vec<u8>> = HashMap::new();

  for (filename, content) in &files_data {
    let file_path = temp_dir.path().join(filename);
    fs::write(&file_path, content).unwrap();
    expected_files.insert(*filename, content.to_vec());
  }

  // Add a file in subdirectory
  let nested_file = subdir.join("nested.txt");
  let nested_content = b"Nested content";
  fs::write(&nested_file, nested_content).unwrap();
  expected_files.insert("subdir/nested.txt", nested_content.to_vec());

  let fs =
    PhysicalFileSystem::new(Uri::from_file_path(temp_dir.path()).unwrap())
      .unwrap();

  let result = fs.iter_files().unwrap();
  let actual_files: Vec<_> = result.collect();

  assert_eq!(actual_files.len(), expected_files.len());

  // Check that all expected files are present
  for (uri, content) in actual_files {
    let path_str = uri.path_str();
    let found =
      expected_files
        .iter()
        .any(|(expected_path, expected_content)| {
          path_str.ends_with(*expected_path) && content == *expected_content
        });
    assert!(
      found,
      "Unexpected file found: {} with {} bytes",
      uri,
      content.len()
    );
  }
}

#[test]
fn test_physical_fs_iter_files_ignores_directories() {
  let temp_dir = TempDir::new().unwrap();

  // Create empty directory
  let empty_dir = temp_dir.path().join("empty_dir");
  fs::create_dir(&empty_dir).unwrap();

  // Create file
  let file_path = temp_dir.path().join("test.txt");
  let content = b"Test content";
  fs::write(&file_path, content).unwrap();

  let fs =
    PhysicalFileSystem::new(Uri::from_file_path(temp_dir.path()).unwrap())
      .unwrap();

  let result = fs.iter_files().unwrap();
  let files: Vec<_> = result.collect();

  // Should only get the file, not the directory
  assert_eq!(files.len(), 1);
  assert_eq!(files[0].1, content);
}

#[test]
fn test_fs_wrapper_iter_files_memory() {
  let memory_fs = MemoryFileSystem::new();

  let file_uri = memory_fs.uri().join("test.txt").unwrap();
  let content = b"Wrapper test";
  memory_fs.write(&file_uri, content).unwrap();

  let result = memory_fs.iter_files().unwrap();
  let files: Vec<_> = result.collect();

  assert_eq!(files.len(), 1);
  assert_eq!(files[0].0, file_uri);
  assert_eq!(files[0].1, content);
}

#[test]
fn test_fs_wrapper_iter_files_physical() {
  let temp_dir = TempDir::new().unwrap();
  let file_path = temp_dir.path().join("test.txt");
  let content = b"Physical wrapper test";

  fs::write(&file_path, content).unwrap();

  let physical_fs =
    PhysicalFileSystem::new(Uri::from_file_path(temp_dir.path()).unwrap())
      .unwrap();

  let result = physical_fs.iter_files().unwrap();
  let files: Vec<_> = result.collect();

  assert_eq!(files.len(), 1);
  assert_eq!(files[0].1, content);
}

#[test]
fn test_iter_files_with_various_extensions() {
  let fs = MemoryFileSystem::new();

  let files_data: Vec<(&str, &[u8])> = vec![
    ("document.txt", b"Text file"),
    ("config.json", b"{\"key\": \"value\"}"),
    ("script.js", b"console.log('hello');"),
    ("style.css", b"body { margin: 0; }"),
    ("image.png", b"\x89PNG\r\n\x1a\n"), // PNG header
    ("no_extension", b"File without extension"),
  ];

  let mut expected_files = HashMap::new();

  for (filename, content) in &files_data {
    let uri = fs.uri().join(filename).unwrap();
    fs.write(&uri, content).unwrap();
    expected_files.insert(uri, content.to_vec());
  }

  let result = fs.iter_files().unwrap();
  let actual_files: HashMap<Uri, Vec<u8>> = result.collect();

  assert_eq!(actual_files.len(), expected_files.len());
  for (uri, content) in expected_files {
    assert_eq!(actual_files.get(&uri), Some(&content));
  }
}

#[test]
fn test_iter_files_large_file() {
  let fs = MemoryFileSystem::new();

  // Create a larger file (1MB)
  let large_content: Vec<u8> =
    (0..1024 * 1024).map(|i| (i % 256) as u8).collect();
  let file_uri = fs.uri().join("large_file.dat").unwrap();
  fs.write(&file_uri, &large_content).unwrap();

  let result = fs.iter_files().unwrap();
  let files: Vec<_> = result.collect();

  assert_eq!(files.len(), 1);
  assert_eq!(files[0].0, file_uri);
  assert_eq!(files[0].1, large_content);
}