From 39decd08ae89bf8dcae333d64efc3dba65803651 Mon Sep 17 00:00:00 2001
From: Fedor Telnov <f.telnov@picodata.io>
Date: Thu, 29 Jun 2023 23:49:49 +0300
Subject: [PATCH] feat: in the process of reworking

---
 Cargo.lock                          | 69 +++++------------------
 Cargo.toml                          |  3 +-
 Makefile                            | 11 +++-
 example/Cargo.toml                  |  2 +-
 example/src/lib.rs                  | 40 ++++++++------
 tarantool-test/Cargo.toml           | 10 +---
 tarantool-test/src/cli.rs           | 32 +++++++++++
 tarantool-test/src/default_init.lua | 37 -------------
 tarantool-test/src/lib.rs           | 17 +++---
 tarantool-test/src/main.rs          | 57 -------------------
 tests/Cargo.toml                    | 11 ++++
 tests/src/lib.rs                    | 86 +++++++++++++++++++++++++++++
 12 files changed, 189 insertions(+), 186 deletions(-)
 create mode 100644 tarantool-test/src/cli.rs
 delete mode 100644 tarantool-test/src/default_init.lua
 delete mode 100644 tarantool-test/src/main.rs
 create mode 100644 tests/Cargo.toml
 create mode 100644 tests/src/lib.rs

diff --git a/Cargo.lock b/Cargo.lock
index 3ff65e9..e83aee2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -445,15 +445,6 @@ dependencies = [
  "tarantool-test",
 ]
 
-[[package]]
-name = "fastrand"
-version = "1.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
-dependencies = [
- "instant",
-]
-
 [[package]]
 name = "flate2"
 version = "1.0.26"
@@ -669,15 +660,6 @@ dependencies = [
  "hashbrown 0.14.0",
 ]
 
-[[package]]
-name = "instant"
-version = "0.1.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
-dependencies = [
- "cfg-if 1.0.0",
-]
-
 [[package]]
 name = "io-lifetimes"
 version = "1.0.11"
@@ -734,16 +716,7 @@ version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "edd4ad156b9934dc21cad96fd17278a7cb6f30a5657a9d976cd7b71d6d49c02c"
 dependencies = [
- "linkme-impl 0.2.10",
-]
-
-[[package]]
-name = "linkme"
-version = "0.3.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97f3302efc6ebb7b5f0810a1096dbbb44a536711a4576bc89264a8f9a1d634d8"
-dependencies = [
- "linkme-impl 0.3.10",
+ "linkme-impl",
 ]
 
 [[package]]
@@ -757,17 +730,6 @@ dependencies = [
  "syn 1.0.109",
 ]
 
-[[package]]
-name = "linkme-impl"
-version = "0.3.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "279a77bf40c85a08513aca203635b96610ebf0e37a92cb0cee76e04da100a426"
-dependencies = [
- "proc-macro2 1.0.63",
- "quote 1.0.28",
- "syn 2.0.22",
-]
-
 [[package]]
 name = "linux-raw-sys"
 version = "0.1.4"
@@ -1451,7 +1413,7 @@ dependencies = [
  "dlopen",
  "futures",
  "libc",
- "linkme 0.2.10",
+ "linkme",
  "log",
  "nix",
  "num-derive",
@@ -1490,29 +1452,14 @@ dependencies = [
 name = "tarantool-test"
 version = "0.1.0"
 dependencies = [
+ "anyhow",
  "clap",
- "linkme 0.3.10",
  "serde",
  "serde_json",
  "tarantool",
- "tempfile",
  "tester",
 ]
 
-[[package]]
-name = "tempfile"
-version = "3.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6"
-dependencies = [
- "autocfg",
- "cfg-if 1.0.0",
- "fastrand",
- "redox_syscall 0.3.5",
- "rustix 0.37.20",
- "windows-sys 0.48.0",
-]
-
 [[package]]
 name = "term"
 version = "0.6.1"
@@ -1534,6 +1481,16 @@ dependencies = [
  "term",
 ]
 
+[[package]]
+name = "tests"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "example",
+ "serde",
+ "serde_json",
+]
+
 [[package]]
 name = "thiserror"
 version = "1.0.40"
diff --git a/Cargo.toml b/Cargo.toml
index 699ba2a..3aae7a5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -2,5 +2,6 @@
 
 members = [
     "tarantool-test",
-    "example"
+    "example",
+    "tests"
 ]
diff --git a/Makefile b/Makefile
index 32f674b..d858866 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,14 @@
+ifeq ($(OS), Linux)
+	LIBS_EXT = so
+else
+	ifeq ($(OS), Darwin)
+		LIBS_EXT = dylib
+	endif
+endif
+
+
 build-example:
 	cargo build -p example
 
 run-example: build-example
-	cargo run --features="bin" -p tarantool-test -- -p libexample
+	tarantool-runner -p ./target/debug/libexample.$(LIBS_EXT) -e cli_test_suite_entrypoint -- --filter 
\ No newline at end of file
diff --git a/example/Cargo.toml b/example/Cargo.toml
index 4192ec6..1a4b09a 100644
--- a/example/Cargo.toml
+++ b/example/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2021"
 
 [dependencies]
 tarantool = { version = "1.1.0", features = ["test"] }
-tarantool-test = { path = "../tarantool-test" }
+tarantool-test = { path = "../tarantool-test", features = ["cli"] }
 anyhow = "1"
 shors = "0.4"
 
diff --git a/example/src/lib.rs b/example/src/lib.rs
index e1cc7c8..192ce97 100644
--- a/example/src/lib.rs
+++ b/example/src/lib.rs
@@ -3,7 +3,9 @@ use std::ffi::c_int;
 use anyhow::Result;
 use shors::shors_info;
 use tarantool::tlua::ffi::lua_State;
-use tarantool_test::{default_tests_execute, SuiteConfig, TestsSetupConfig};
+use tarantool_test::{
+    cli::parse_tests_config_from_cli, execute_tests_with_suite, SuiteConfig, TestsConfig,
+};
 
 #[tarantool::test]
 fn test_example() {
@@ -15,29 +17,31 @@ fn test_panic_example() {
     assert_eq!(1, 0)
 }
 
+fn before_all() {
+    shors_info!("before_all executed")
+}
+fn after_all() {
+    shors_info!("after_all executed")
+}
+fn before_each() {
+    shors_info!("before_each executed")
+}
+fn after_each() {
+    shors_info!("after_each executed")
+}
+
 #[tarantool::proc]
-pub fn tarantool_test_entrypoint(config: TestsSetupConfig) -> Result<()> {
-    fn before_all() {
-        shors_info!("before_all executed")
-    }
-    fn after_all() {
-        shors_info!("after_all executed")
-    }
-    fn before_each() {
-        shors_info!("before_each executed")
-    }
-    fn after_each() {
-        shors_info!("after_each executed")
-    }
-
-    default_tests_execute(SuiteConfig {
-        setup: config,
+pub fn cli_test_suite_entrypoint(input: String) -> Result<()> {
+    let tests_config = parse_tests_config_from_cli(input)?;
+
+    execute_tests_with_suite(SuiteConfig {
+        tests_cfg: tests_config,
         before_all,
         after_all,
         before_each,
         after_each,
     })
-    .expect("default test suite failed");
+    .expect("cli-based test suite failed");
 
     Ok(())
 }
diff --git a/tarantool-test/Cargo.toml b/tarantool-test/Cargo.toml
index e06a4de..cdae912 100644
--- a/tarantool-test/Cargo.toml
+++ b/tarantool-test/Cargo.toml
@@ -4,19 +4,15 @@ version = "0.1.0"
 edition = "2021"
 
 [features]
-bin = ["dep:clap", "dep:serde_json", "dep:tempfile"]
+# Introduces CLI interface for suite.
+cli = ["dep:clap", "dep:serde_json"]
 
 [dependencies]
-linkme = "0.3"
 tarantool = {version = "1.1.0", features = ["test"] }
 serde = "1.0"
 tester = "0.7"
+anyhow = "1"
 
 # Binary deps.
 clap = {version = "4", features = ["derive"], optional = true}
 serde_json = {version = "1", optional = true}
-tempfile = {version = "3", optional = true }
-
-[[bin]]
-name = "tarantool-test"
-required-features = ["bin"]
\ No newline at end of file
diff --git a/tarantool-test/src/cli.rs b/tarantool-test/src/cli.rs
new file mode 100644
index 0000000..0d90418
--- /dev/null
+++ b/tarantool-test/src/cli.rs
@@ -0,0 +1,32 @@
+use anyhow::Result;
+use std::{
+    fs::{create_dir, File},
+    io::Write,
+    process::Command,
+};
+
+use clap::Parser;
+
+use crate::{SuiteConfig, TestsConfig};
+
+#[derive(Parser)]
+#[command(author, version, about, long_about = None)]
+struct Cli {
+    #[arg(
+        short,
+        long,
+        help = "Filter out needed tests by passing their fullname or even part of the name."
+    )]
+    filter: Option<String>,
+}
+
+impl From<Cli> for TestsConfig {
+    fn from(cli: Cli) -> Self {
+        Self { filter: cli.filter }
+    }
+}
+
+pub fn parse_tests_config_from_cli(cli_input: String) -> Result<TestsConfig> {
+    let cli = Cli::try_parse_from(std::iter::once(cli_input))?;
+    Ok(cli.into())
+}
diff --git a/tarantool-test/src/default_init.lua b/tarantool-test/src/default_init.lua
deleted file mode 100644
index 1c18a50..0000000
--- a/tarantool-test/src/default_init.lua
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env tarantool
-
-local fio = require('fio')
-
-local test_dir = os.environ()["TARANTOOL_TEST_DIR"]
-local tmpdir = test_dir .. "/tmp"
-local test_package = os.environ()["TARANTOOL_TEST_PACKAGE"]
-local package_entrypoint = test_package .. ".tarantool_test_entrypoint"
-local cfg = require("json").decode(os.environ()["TARANTOOL_TEST_CONFIG"])
-
-box.cfg {
-    listen = 3301,
-    wal_mode = 'none',
-    memtx_dir = tmpdir,
-    election_mode = 'manual',
-}
-
-box.ctl.promote()
-
-fio.rmtree(tmpdir)
-
--- Add executable location to lua search path
-package.cpath = 'target/debug/?.so;target/debug/?.dylib;' .. package.cpath
-require(test_package)
-
--- Run tests
-box.schema.func.create(package_entrypoint, { language = 'C', if_not_exists = true })
-
-local function exec_tests()
-    box.func[package_entrypoint]:call({cfg})
-end
-
-if not pcall(exec_tests) then
-    require("log").error('failed to execute tests, exiting with error code 1')
-    os.exit(1)
-end
-os.exit(0)
diff --git a/tarantool-test/src/lib.rs b/tarantool-test/src/lib.rs
index bbda539..7232838 100644
--- a/tarantool-test/src/lib.rs
+++ b/tarantool-test/src/lib.rs
@@ -4,17 +4,18 @@ use serde::{Deserialize, Serialize};
 use tarantool::test::test_cases;
 use tester::{TestDescAndFn, TestFn, TestOpts};
 
-pub use linkme;
+#[cfg(feature = "cli")]
+pub mod cli;
 
 #[derive(Clone, Deserialize, Serialize, Debug, Default)]
-pub struct TestsSetupConfig {
+pub struct TestsConfig {
     #[serde(default)]
     pub filter: Option<String>,
 }
 
 #[derive(Clone, Debug)]
 pub struct SuiteConfig {
-    pub setup: TestsSetupConfig,
+    pub tests_cfg: TestsConfig,
     pub before_all: fn(),
     pub after_all: fn(),
     pub before_each: fn(),
@@ -25,7 +26,7 @@ impl Default for SuiteConfig {
     fn default() -> Self {
         fn noop() {}
         Self {
-            setup: Default::default(),
+            tests_cfg: Default::default(),
             before_all: noop,
             after_all: noop,
             before_each: noop,
@@ -45,7 +46,7 @@ pub fn collect_tests(suite: &SuiteConfig) -> Vec<TestDescAndFn> {
         .iter()
         .filter(|case| {
             suite
-                .setup
+                .tests_cfg
                 .filter
                 .as_ref()
                 .map(|must_contains| case.name().contains(must_contains))
@@ -67,9 +68,9 @@ pub fn collect_tests(suite: &SuiteConfig) -> Vec<TestDescAndFn> {
 /// Executes tests with given suite in an ordinary manner.
 ///
 /// Most of the time, if you don't want to modify the test system behavior, you are happy with this method.
-pub fn default_tests_execute(mut suite: SuiteConfig) -> Result<(), Box<dyn Error>> {
-    if suite.setup.filter == Some("".to_string()) {
-        suite.setup.filter = None
+pub fn execute_tests_with_suite(mut suite: SuiteConfig) -> Result<(), Box<dyn Error>> {
+    if suite.tests_cfg.filter == Some("".to_string()) {
+        suite.tests_cfg.filter = None
     }
 
     let opts = &TestOpts {
diff --git a/tarantool-test/src/main.rs b/tarantool-test/src/main.rs
deleted file mode 100644
index ee7e8d7..0000000
--- a/tarantool-test/src/main.rs
+++ /dev/null
@@ -1,57 +0,0 @@
-use std::{
-    fs::{create_dir, File},
-    io::Write,
-    process::Command,
-};
-
-use clap::Parser;
-use tarantool_test::TestsSetupConfig;
-use tempfile::tempdir;
-
-const DEFAULT_LUA_INIT: &str = include_str!("default_init.lua");
-
-#[derive(Parser)]
-#[command(author, version, about, long_about = None)]
-struct Cli {
-    #[arg(
-        short,
-        long,
-        help = r#"Package where tests would be executed. Package file must follow rules stated in the docs."#
-    )]
-    package: String,
-
-    #[arg(
-        short,
-        long,
-        help = "Filter out needed tests by passing their fullname or even part of the name."
-    )]
-    filter: Option<String>,
-}
-
-fn main() {
-    let cli = Cli::parse();
-
-    let setup_config = TestsSetupConfig { filter: cli.filter };
-    let serialized_config =
-        serde_json::to_string(&setup_config).expect("failed to serialize test config");
-
-    let tmp_dir = tempdir().expect("failed to create tempdir");
-    let tarantool_init_file = tmp_dir.path().join("init.lua");
-    let tarantool_tmpdir = tmp_dir.path().join("tmp");
-
-    create_dir(tarantool_tmpdir.as_path()).unwrap();
-    let mut file = File::create(&tarantool_init_file).unwrap();
-    file.write_all(DEFAULT_LUA_INIT.as_bytes()).unwrap();
-
-    let status = Command::new("tarantool")
-        .arg(tarantool_init_file)
-        .env("TARANTOOL_TEST_PACKAGE", cli.package)
-        .env("TARANTOOL_TEST_DIR", tmp_dir.path())
-        .env("TARANTOOL_TEST_CONFIG", serialized_config)
-        .status()
-        .expect("failed to get status code from executed tarantool part");
-    assert!(
-        status.success(),
-        "exit code of the tarantool part is not success: {status:?}"
-    );
-}
diff --git a/tests/Cargo.toml b/tests/Cargo.toml
new file mode 100644
index 0000000..ff823e7
--- /dev/null
+++ b/tests/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "tests"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+serde = "1.0"
+anyhow = "1"
+
+serde_json = "1"
+example = {path = "../example"}
\ No newline at end of file
diff --git a/tests/src/lib.rs b/tests/src/lib.rs
new file mode 100644
index 0000000..d0c8a00
--- /dev/null
+++ b/tests/src/lib.rs
@@ -0,0 +1,86 @@
+#![cfg(test)]
+use std::process::{Command, ExitStatus};
+
+use anyhow::Result;
+
+/// Determines package extension of the prepared package.
+fn package_extension() -> &'static str {
+    if cfg!(target_os = "macos") {
+        "dylib"
+    } else {
+        "so"
+    }
+}
+
+fn example_crate_run(entrypoint: &str, input: &str) -> Command {
+    let path_to_package = format!("../target/debug/libexample.{}", package_extension());
+
+    let mut command = Command::new("../target/debug/tarantool-runner");
+
+    command
+        .arg("run")
+        .arg("-p")
+        .arg(path_to_package)
+        .arg("-e")
+        .arg(entrypoint)
+        .arg("--")
+        .arg(input);
+
+    command
+}
+
+#[allow(unused)]
+#[derive(Debug, Clone)]
+struct CommandResult {
+    status: ExitStatus,
+    stdout: String,
+    stderr: String,
+}
+
+impl CommandResult {
+    fn from_command(command: &mut Command) -> Result<CommandResult> {
+        let output = command.output()?;
+        Ok(CommandResult {
+            status: command.status()?,
+            stdout: String::from_utf8(output.stdout)?,
+            stderr: String::from_utf8(output.stderr)?,
+        })
+    }
+
+    fn assert_status_success(&self, success: bool) {
+        assert_eq!(
+            self.status.success(),
+            success,
+            "command status invalid: {:?}, command result is: {:?}",
+            self.status,
+            self
+        )
+    }
+}
+
+#[test]
+fn it_tests_entrypoint_with_input() -> Result<()> {
+    let input = EntrypointInput {
+        field_a: "field_a_content".to_string(),
+        field_b: 10,
+    };
+
+    let serialized = serde_json::to_string(&input).unwrap();
+    let mut command = example_crate_run("entrypoint_with_input", &serialized);
+    let result = CommandResult::from_command(&mut command)?;
+    result.assert_status_success(true);
+
+    assert!(result.stdout.contains(&format!("{:?}", input)));
+
+    Ok(())
+}
+
+#[test]
+fn it_tests_panicking_entrypoint() -> Result<()> {
+    let mut command = example_crate_run("panicking_entrypoint", "");
+    let result = CommandResult::from_command(&mut command)?;
+    result.assert_status_success(false);
+
+    assert!(result.stdout.contains("must be seen"));
+    Ok(())
+}
-- 
GitLab