#![cfg(feature = "dataframe")]
#![allow(missing_docs)]
use std::fs;
use std::path::Path;
#[test]
fn test_std_007_from_columns_single() {
let result = ruchy::stdlib::dataframe::from_columns(vec![("age", vec![25, 30, 35])]);
assert!(result.is_ok(), "from_columns should succeed");
let df = result.unwrap();
let shape = df.shape();
assert_eq!(shape.0, 3, "Should have 3 rows");
assert_eq!(shape.1, 1, "Should have 1 column");
let columns = df.get_column_names();
assert_eq!(columns.len(), 1, "Should have 1 column name");
assert_eq!(columns[0], "age", "Column name should be 'age'");
}
#[test]
fn test_std_007_from_columns_multiple() {
let result = ruchy::stdlib::dataframe::from_columns(vec![
("name", vec![1, 2, 3]), ("age", vec![25, 30, 35]),
]);
assert!(result.is_ok(), "from_columns should succeed");
let df = result.unwrap();
let shape = df.shape();
assert_eq!(shape.0, 3, "Should have 3 rows");
assert_eq!(shape.1, 2, "Should have 2 columns");
let columns = df.get_column_names();
assert_eq!(columns.len(), 2, "Should have 2 column names");
assert!(
columns.iter().any(|c| c.as_str() == "name"),
"Should have 'name' column"
);
assert!(
columns.iter().any(|c| c.as_str() == "age"),
"Should have 'age' column"
);
}
#[test]
fn test_std_007_from_columns_empty() {
let result = ruchy::stdlib::dataframe::from_columns(vec![]);
assert!(
result.is_ok(),
"from_columns should succeed with empty input"
);
let df = result.unwrap();
let shape = df.shape();
assert_eq!(shape.0, 0, "Should have 0 rows");
assert_eq!(shape.1, 0, "Should have 0 columns");
}
#[test]
fn test_std_007_from_columns_mismatched_lengths() {
let result = ruchy::stdlib::dataframe::from_columns(vec![
("age", vec![25, 30]),
("score", vec![95, 87, 92]), ]);
assert!(
result.is_err(),
"from_columns should fail with mismatched lengths"
);
let error = result.unwrap_err();
assert!(!error.is_empty(), "Error message should not be empty");
assert!(
error.contains("length") || error.contains("mismatch") || error.contains("same"),
"Error should mention length mismatch"
);
}
#[test]
fn test_std_007_read_csv_valid() {
let test_csv = "/tmp/test_std_007_read.csv";
fs::write(test_csv, "age,score\n25,95\n30,87\n35,92\n").unwrap();
let result = ruchy::stdlib::dataframe::read_csv(test_csv);
assert!(result.is_ok(), "read_csv should succeed");
let df = result.unwrap();
let shape = df.shape();
assert_eq!(shape.0, 3, "Should have 3 rows");
assert_eq!(shape.1, 2, "Should have 2 columns");
let columns = df.get_column_names();
assert!(
columns.iter().any(|c| c.as_str() == "age"),
"Should have 'age' column"
);
assert!(
columns.iter().any(|c| c.as_str() == "score"),
"Should have 'score' column"
);
fs::remove_file(test_csv).ok();
}
#[test]
fn test_std_007_read_csv_not_found() {
let result = ruchy::stdlib::dataframe::read_csv("/tmp/nonexistent_file_xyz_12345.csv");
assert!(
result.is_err(),
"read_csv should fail for non-existent file"
);
let error = result.unwrap_err();
assert!(!error.is_empty(), "Error message should not be empty");
}
#[test]
fn test_std_007_write_csv_success() {
let mut df = ruchy::stdlib::dataframe::from_columns(vec![
("age", vec![25, 30, 35]),
("score", vec![95, 87, 92]),
])
.unwrap();
let test_csv = "/tmp/test_std_007_write.csv";
let result = ruchy::stdlib::dataframe::write_csv(&mut df, test_csv);
assert!(result.is_ok(), "write_csv should succeed");
assert!(Path::new(test_csv).exists(), "CSV file should exist");
let content = fs::read_to_string(test_csv).unwrap();
assert!(!content.is_empty(), "CSV should not be empty");
assert!(content.contains("age"), "CSV should contain 'age' column");
assert!(
content.contains("score"),
"CSV should contain 'score' column"
);
fs::remove_file(test_csv).ok();
}
#[test]
fn test_std_007_select_single_column() {
let df = ruchy::stdlib::dataframe::from_columns(vec![
("age", vec![25, 30, 35]),
("score", vec![95, 87, 92]),
])
.unwrap();
let result = ruchy::stdlib::dataframe::select(&df, &["age"]);
assert!(result.is_ok(), "select should succeed");
let selected = result.unwrap();
let shape = selected.shape();
assert_eq!(shape.0, 3, "Should preserve row count");
assert_eq!(shape.1, 1, "Should have 1 column");
let columns = selected.get_column_names();
assert_eq!(columns.len(), 1, "Should have 1 column");
assert_eq!(columns[0], "age", "Column should be 'age'");
}
#[test]
fn test_std_007_select_multiple_columns() {
let df = ruchy::stdlib::dataframe::from_columns(vec![
("name", vec![1, 2, 3]),
("age", vec![25, 30, 35]),
("score", vec![95, 87, 92]),
])
.unwrap();
let result = ruchy::stdlib::dataframe::select(&df, &["age", "score"]);
assert!(result.is_ok(), "select should succeed");
let selected = result.unwrap();
let shape = selected.shape();
assert_eq!(shape.0, 3, "Should preserve row count");
assert_eq!(shape.1, 2, "Should have 2 columns");
let columns = selected.get_column_names();
assert_eq!(columns.len(), 2, "Should have 2 columns");
assert!(
columns.iter().any(|c| c.as_str() == "age"),
"Should have 'age'"
);
assert!(
columns.iter().any(|c| c.as_str() == "score"),
"Should have 'score'"
);
}
#[test]
fn test_std_007_select_nonexistent_column() {
let df = ruchy::stdlib::dataframe::from_columns(vec![("age", vec![25, 30, 35])]).unwrap();
let result = ruchy::stdlib::dataframe::select(&df, &["nonexistent"]);
assert!(
result.is_err(),
"select should fail for non-existent column"
);
let error = result.unwrap_err();
assert!(!error.is_empty(), "Error message should not be empty");
}
#[test]
fn test_std_007_head_normal() {
let df =
ruchy::stdlib::dataframe::from_columns(vec![("age", vec![25, 30, 35, 40, 45])]).unwrap();
let result = ruchy::stdlib::dataframe::head(&df, 3);
assert!(result.is_ok(), "head should succeed");
let top = result.unwrap();
let shape = top.shape();
assert_eq!(shape.0, 3, "Should have 3 rows");
assert_eq!(shape.1, 1, "Should preserve column count");
}
#[test]
fn test_std_007_head_exceeds_rows() {
let df = ruchy::stdlib::dataframe::from_columns(vec![("age", vec![25, 30])]).unwrap();
let result = ruchy::stdlib::dataframe::head(&df, 10);
assert!(result.is_ok(), "head should succeed even when n > rows");
let top = result.unwrap();
let shape = top.shape();
assert_eq!(shape.0, 2, "Should return all rows");
}
#[test]
fn test_std_007_tail_normal() {
let df =
ruchy::stdlib::dataframe::from_columns(vec![("age", vec![25, 30, 35, 40, 45])]).unwrap();
let result = ruchy::stdlib::dataframe::tail(&df, 3);
assert!(result.is_ok(), "tail should succeed");
let bottom = result.unwrap();
let shape = bottom.shape();
assert_eq!(shape.0, 3, "Should have 3 rows");
assert_eq!(shape.1, 1, "Should preserve column count");
}
#[test]
fn test_std_007_tail_exceeds_rows() {
let df = ruchy::stdlib::dataframe::from_columns(vec![("age", vec![25, 30])]).unwrap();
let result = ruchy::stdlib::dataframe::tail(&df, 10);
assert!(result.is_ok(), "tail should succeed even when n > rows");
let bottom = result.unwrap();
let shape = bottom.shape();
assert_eq!(shape.0, 2, "Should return all rows");
}
#[test]
fn test_std_007_shape() {
let df = ruchy::stdlib::dataframe::from_columns(vec![
("age", vec![25, 30, 35]),
("score", vec![95, 87, 92]),
])
.unwrap();
let result = ruchy::stdlib::dataframe::shape(&df);
assert!(result.is_ok(), "shape should succeed");
let (rows, cols) = result.unwrap();
assert_eq!(rows, 3, "Should have 3 rows");
assert_eq!(cols, 2, "Should have 2 columns");
}
#[test]
fn test_std_007_shape_empty() {
let df = ruchy::stdlib::dataframe::from_columns(vec![]).unwrap();
let result = ruchy::stdlib::dataframe::shape(&df);
assert!(result.is_ok(), "shape should succeed for empty DataFrame");
let (rows, cols) = result.unwrap();
assert_eq!(rows, 0, "Should have 0 rows");
assert_eq!(cols, 0, "Should have 0 columns");
}
#[test]
fn test_std_007_columns() {
let df = ruchy::stdlib::dataframe::from_columns(vec![
("name", vec![1, 2, 3]),
("age", vec![25, 30, 35]),
])
.unwrap();
let result = ruchy::stdlib::dataframe::columns(&df);
assert!(result.is_ok(), "columns should succeed");
let names = result.unwrap();
assert_eq!(names.len(), 2, "Should have 2 column names");
assert!(names.contains(&"name".to_string()), "Should contain 'name'");
assert!(names.contains(&"age".to_string()), "Should contain 'age'");
}
#[test]
fn test_std_007_row_count() {
let df = ruchy::stdlib::dataframe::from_columns(vec![("age", vec![25, 30, 35, 40])]).unwrap();
let result = ruchy::stdlib::dataframe::row_count(&df);
assert!(result.is_ok(), "row_count should succeed");
let count = result.unwrap();
assert_eq!(count, 4, "Should have 4 rows");
}
#[test]
fn test_std_007_row_count_empty() {
let df = ruchy::stdlib::dataframe::from_columns(vec![]).unwrap();
let result = ruchy::stdlib::dataframe::row_count(&df);
assert!(
result.is_ok(),
"row_count should succeed for empty DataFrame"
);
let count = result.unwrap();
assert_eq!(count, 0, "Should have 0 rows");
}
#[cfg(test)]
mod property_tests {
use proptest::prelude::*;
proptest! {
#![proptest_config(ProptestConfig::with_cases(20))]
#[test]
fn test_std_007_csv_roundtrip(rows in 1usize..10, cols in 1usize..5) {
let col_names: Vec<String> = (0..cols).map(|i| format!("col{i}")).collect();
let columns: Vec<(&str, Vec<i64>)> = col_names.iter()
.enumerate()
.map(|(i, name)| {
let values: Vec<i64> = (0..rows).map(|j| (i * 100 + j).try_into().expect("test values fit in i64")).collect();
(name.as_str(), values)
})
.collect();
let mut df = ruchy::stdlib::dataframe::from_columns(columns).unwrap();
let original_shape = df.shape();
let test_file = format!("/tmp/test_std_007_roundtrip_{rows}_{cols}.csv");
ruchy::stdlib::dataframe::write_csv(&mut df, &test_file).unwrap();
let df_read = ruchy::stdlib::dataframe::read_csv(&test_file).unwrap();
let read_shape = df_read.shape();
std::fs::remove_file(&test_file).ok();
prop_assert_eq!(original_shape.0, read_shape.0, "Rows should be preserved");
prop_assert_eq!(original_shape.1, read_shape.1, "Columns should be preserved");
}
#[test]
fn test_std_007_never_panics_select(n_cols in 1usize..10) {
let col_names: Vec<String> = (0..n_cols).map(|i| format!("col{i}")).collect();
let columns: Vec<(&str, Vec<i64>)> = col_names.iter()
.map(|name| (name.as_str(), vec![1, 2, 3]))
.collect();
let df = ruchy::stdlib::dataframe::from_columns(columns).unwrap();
let _ = ruchy::stdlib::dataframe::select(&df, &["nonexistent"]);
}
#[test]
fn test_std_007_head_tail_consistency(n_rows in 2usize..20, take in 1usize..10) {
let df = ruchy::stdlib::dataframe::from_columns(vec![
("col1", (0..n_rows).map(|x| x.try_into().expect("test values fit in i64")).collect()),
("col2", (0..n_rows).map(|x| (x * 2).try_into().expect("test values fit in i64")).collect()),
]).unwrap();
let original_cols = df.shape().1;
let head_df = ruchy::stdlib::dataframe::head(&df, take).unwrap();
let tail_df = ruchy::stdlib::dataframe::tail(&df, take).unwrap();
prop_assert_eq!(head_df.shape().1, original_cols, "head should preserve columns");
prop_assert_eq!(tail_df.shape().1, original_cols, "tail should preserve columns");
}
}
}