From c39a3561cefd5d0b2d5d5f281a55f6b6a863f235 Mon Sep 17 00:00:00 2001 From: Noy Date: Wed, 27 Nov 2024 18:02:23 +0800 Subject: [PATCH] feat: complete postgres client compatibility tests (#111) --- compatibility/pg/c/pg_test.c | 5 +- compatibility/pg/clean.sh | 7 + compatibility/pg/csharp/PGTest.cs | 179 ++++++++++++++++++++++++++ compatibility/pg/csharp/PGTest.csproj | 12 ++ compatibility/pg/go/pg.go | 179 ++++++++++++++++++++++++++ compatibility/pg/java/PGTest.java | 6 +- compatibility/pg/node/pg_test.js | 6 +- compatibility/pg/perl/pg_test.pl | 18 ++- compatibility/pg/php/pg_test.php | 6 +- compatibility/pg/python/pg_test.py | 18 +-- compatibility/pg/r/PGTest.R | 114 ++++++++++++++++ compatibility/pg/ruby/pg_test.rb | 8 +- compatibility/pg/rust/Cargo.toml | 9 ++ compatibility/pg/rust/pg_test.rs | 128 ++++++++++++++++++ compatibility/pg/test.bats | 96 ++++++++------ compatibility/pg/test.data | 2 +- 16 files changed, 731 insertions(+), 62 deletions(-) create mode 100644 compatibility/pg/clean.sh create mode 100644 compatibility/pg/csharp/PGTest.cs create mode 100644 compatibility/pg/csharp/PGTest.csproj create mode 100644 compatibility/pg/go/pg.go create mode 100644 compatibility/pg/r/PGTest.R create mode 100644 compatibility/pg/rust/Cargo.toml create mode 100644 compatibility/pg/rust/pg_test.rs diff --git a/compatibility/pg/c/pg_test.c b/compatibility/pg/c/pg_test.c index 64e42008..08170990 100644 --- a/compatibility/pg/c/pg_test.c +++ b/compatibility/pg/c/pg_test.c @@ -81,10 +81,7 @@ int runTests(PGTest *pgTest) { size_t removeNewline(char *line) { size_t len = strlen(line); - if (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r')) { - line[--len] = '\0'; - } - if (len > 0 && line[len - 1] == '\r') { + while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r')) { line[--len] = '\0'; } return len; diff --git a/compatibility/pg/clean.sh b/compatibility/pg/clean.sh new file mode 100644 index 00000000..b8fc0a67 --- /dev/null +++ b/compatibility/pg/clean.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +rm -rf ./c/pg_test \ + ./csharp/bin ./csharp/obj \ + ./go/pg \ + ./java/*.class \ + ./rust/target ./rust/Cargo.lock \ No newline at end of file diff --git a/compatibility/pg/csharp/PGTest.cs b/compatibility/pg/csharp/PGTest.cs new file mode 100644 index 00000000..9d868ea9 --- /dev/null +++ b/compatibility/pg/csharp/PGTest.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections.Generic; +using System.Data; +using Microsoft.Data.SqlClient; +using System.IO; + +public class PGTest +{ + public class Tests + { + private SqlConnection conn; + private SqlCommand cmd; + private List tests = new List(); + + public void Connect(string ip, int port, string user, string password) + { + try + { + string connectionString = $"Server={ip},{port};User Id={user};Password={password};"; + conn = new SqlConnection(connectionString); + conn.Open(); + cmd = conn.CreateCommand(); + cmd.CommandType = CommandType.Text; + } + catch (SqlException e) + { + throw new Exception(e.Message); + } + } + + public void Disconnect() + { + try + { + cmd.Dispose(); + conn.Close(); + } + catch (SqlException e) + { + throw new Exception(e.Message); + } + } + + public void AddTest(string query, string[][] expectedResults) + { + tests.Add(new Test(query, expectedResults)); + } + + public bool RunTests() + { + foreach (var test in tests) + { + if (!test.Run(cmd)) + { + return false; + } + } + return true; + } + + public void ReadTestsFromFile(string filename) + { + try + { + using (var reader = new StreamReader(filename)) + { + string line; + while ((line = reader.ReadLine()) != null) + { + if (string.IsNullOrWhiteSpace(line)) continue; + string query = line; + var results = new List(); + while ((line = reader.ReadLine()) != null && !string.IsNullOrWhiteSpace(line)) + { + results.Add(line.Split(',')); + } + string[][] expectedResults = results.ToArray(); + AddTest(query, expectedResults); + } + } + } + catch (IOException e) + { + Console.Error.WriteLine(e.Message); + Environment.Exit(1); + } + } + + public class Test + { + private string query; + private string[][] expectedResults; + + public Test(string query, string[][] expectedResults) + { + this.query = query; + this.expectedResults = expectedResults; + } + + public bool Run(SqlCommand cmd) + { + try + { + Console.WriteLine("Running test: " + query); + cmd.CommandText = query; + using (var reader = cmd.ExecuteReader()) + { + if (!reader.HasRows) + { + if (expectedResults.Length != 0) + { + Console.Error.WriteLine($"Expected {expectedResults.Length} rows, got 0"); + return false; + } + Console.WriteLine("Returns 0 rows"); + return true; + } + if (reader.FieldCount != expectedResults[0].Length) + { + Console.Error.WriteLine($"Expected {expectedResults[0].Length} columns, got {reader.FieldCount}"); + return false; + } + int rows = 0; + while (reader.Read()) + { + for (int col = 0; col < expectedResults[rows].Length; col++) + { + string result = reader.GetString(col); + if (expectedResults[rows][col] != result) + { + Console.Error.WriteLine($"Expected:\n'{expectedResults[rows][col]}'"); + Console.Error.WriteLine($"Result:\n'{result}'\nRest of the results:"); + while (reader.Read()) + { + Console.Error.WriteLine(reader.GetString(0)); + } + return false; + } + } + rows++; + } + Console.WriteLine("Returns " + rows + " rows"); + if (rows != expectedResults.Length) + { + Console.Error.WriteLine($"Expected {expectedResults.Length} rows"); + return false; + } + return true; + } + } + catch (SqlException e) + { + Console.Error.WriteLine(e.Message); + return false; + } + } + } + } + + public static void Main(string[] args) + { + if (args.Length < 5) + { + Console.Error.WriteLine("Usage: PGTest "); + Environment.Exit(1); + } + + var tests = new Tests(); + tests.Connect(args[0], int.Parse(args[1]), args[2], args[3]); + tests.ReadTestsFromFile(args[4]); + + if (!tests.RunTests()) + { + tests.Disconnect(); + Environment.Exit(1); + } + tests.Disconnect(); + } +} \ No newline at end of file diff --git a/compatibility/pg/csharp/PGTest.csproj b/compatibility/pg/csharp/PGTest.csproj new file mode 100644 index 00000000..ef692a92 --- /dev/null +++ b/compatibility/pg/csharp/PGTest.csproj @@ -0,0 +1,12 @@ + + + + Exe + net8.0 + + + + + + + \ No newline at end of file diff --git a/compatibility/pg/go/pg.go b/compatibility/pg/go/pg.go new file mode 100644 index 00000000..3c524e42 --- /dev/null +++ b/compatibility/pg/go/pg.go @@ -0,0 +1,179 @@ +package main + +import ( + "bufio" + "database/sql" + "fmt" + "os" + "strconv" + "strings" + + _ "github.com/lib/pq" +) + +type Test struct { + query string + expectedResults [][]string +} + +type Tests struct { + conn *sql.DB + tests []Test +} + +func (t *Tests) connect(ip string, port int, user, password string) { + connStr := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=postgres sslmode=disable", ip, port, user, password) + var err error + t.conn, err = sql.Open("postgres", connStr) + if err != nil { + panic(err) + } +} + +func (t *Tests) disconnect() { + err := t.conn.Close() + if err != nil { + panic(err) + } +} + +func (t *Tests) addTest(query string, expectedResults [][]string) { + t.tests = append(t.tests, Test{query, expectedResults}) +} + +func (t *Tests) readTestsFromFile(filename string) { + file, err := os.Open(filename) + if err != nil { + panic(err) + } + defer func(file *os.File) { + err := file.Close() + if err != nil { + panic(err) + } + }(file) + + scanner := bufio.NewScanner(file) + var query string + var results [][]string + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if line == "" { + if query != "" { + t.addTest(query, results) + query = "" + results = nil + } + } else if query == "" { + query = line + } else { + results = append(results, strings.Split(line, ",")) + } + } + if query != "" { + t.addTest(query, results) + } + if err := scanner.Err(); err != nil { + panic(err) + } +} + +func (t *Tests) runTests() bool { + for _, test := range t.tests { + if !t.runTest(test) { + return false + } + } + return true +} + +func (t *Tests) runTest(test Test) bool { + fmt.Println("Running test:", test.query) + rows, err := t.conn.Query(test.query) + if err != nil { + fmt.Println("Error executing query:", err) + return false + } + defer func(rows *sql.Rows) { + err := rows.Close() + if err != nil { + panic(err) + } + }(rows) + + columns, err := rows.Columns() + if err != nil { + fmt.Println("Error getting columns:", err) + return false + } + + if len(test.expectedResults) == 0 { + if len(columns) != 0 { + fmt.Printf("Expected 0 columns, got %d\n", len(columns)) + return false + } + fmt.Println("Returns 0 rows") + return true + } + + if len(columns) != len(test.expectedResults[0]) { + fmt.Printf("Expected %d columns, got %d\n", len(test.expectedResults[0]), len(columns)) + return false + } + + var rowCount int + for rows.Next() { + row := make([]string, len(columns)) + rowPointers := make([]interface{}, len(columns)) + for i := range row { + rowPointers[i] = &row[i] + } + if err := rows.Scan(rowPointers...); err != nil { + fmt.Println("Error scanning row:", err) + return false + } + + for i, expected := range test.expectedResults[rowCount] { + if row[i] != expected { + fmt.Printf("Expected: '%s', got: '%s'\n", expected, row[i]) + return false + } + } + rowCount++ + } + + if rowCount != len(test.expectedResults) { + fmt.Printf("Expected %d rows, got %d\n", len(test.expectedResults), rowCount) + return false + } + + fmt.Printf("Returns %d rows\n", rowCount) + return true +} + +func main() { + if len(os.Args) < 6 { + fmt.Println("Usage: pg_test ") + os.Exit(1) + } + + ip := os.Args[1] + port, ok := strconv.Atoi(os.Args[2]) + if ok != nil { + fmt.Println("Invalid port:", os.Args[2]) + os.Exit(1) + } + user := os.Args[3] + password := os.Args[4] + testFile := os.Args[5] + + tests := &Tests{} + tests.connect(ip, port, user, password) + tests.readTestsFromFile(testFile) + + if !tests.runTests() { + tests.disconnect() + os.Exit(1) + } + tests.disconnect() +} diff --git a/compatibility/pg/java/PGTest.java b/compatibility/pg/java/PGTest.java index e605d581..46d1d3ff 100644 --- a/compatibility/pg/java/PGTest.java +++ b/compatibility/pg/java/PGTest.java @@ -73,8 +73,12 @@ public boolean run() { try { System.out.println("Running test: " + query); if (!st.execute(query)) { + if (expectedResults.length != 0) { + System.err.println("Expected " + expectedResults.length + " rows, got 0"); + return false; + } System.out.println("Returns 0 rows"); - return expectedResults.length == 0; + return true; } ResultSet rs = st.getResultSet(); if (rs.getMetaData().getColumnCount() != expectedResults[0].length) { diff --git a/compatibility/pg/node/pg_test.js b/compatibility/pg/node/pg_test.js index d99aa17b..684f9225 100644 --- a/compatibility/pg/node/pg_test.js +++ b/compatibility/pg/node/pg_test.js @@ -12,8 +12,12 @@ class Test { console.log(`Running test: ${this.query}`); const res = await client.query(this.query); if (res.rows.length === 0) { + if (this.expectedResults.length !== 0) { + console.error(`Expected ${this.expectedResults.length} rows, got 0`); + return false; + } console.log("Returns 0 rows"); - return this.expectedResults.length === 0; + return true; } if (res.fields.length !== this.expectedResults[0].length) { console.error(`Expected ${this.expectedResults[0].length} columns, got ${res.fields.length}`); diff --git a/compatibility/pg/perl/pg_test.pl b/compatibility/pg/perl/pg_test.pl index e9eb5868..6d211413 100644 --- a/compatibility/pg/perl/pg_test.pl +++ b/compatibility/pg/perl/pg_test.pl @@ -77,16 +77,24 @@ sub run_test { $st->execute(); my $rows = $st->rows; if ($rows == 0 || $rows == -1) { - print "No rows returned\n"; - return defined $expected_results && @$expected_results == 0; + if (defined $expected_results && @$expected_results == 0) { + print "No rows returned\n"; + return 1; + } + print "Expected " . @$expected_results . " rows, got 0 rows\n"; + return 0; } my $cols = $st->{NUM_OF_FIELDS}; if (!defined $cols) { - print "No columns returned\n"; - return defined $expected_results && @$expected_results == 0; + if (defined $expected_results && @$expected_results == 0) { + print "No columns returned\n"; + return 1; + } + print "Expected " . @{$expected_results->[0]} . " columns, got 0\n"; + return 0; } if ($cols != @{$expected_results->[0]}) { - print "Expected " . @{$expected_results->[0]} . " columns\n"; + print "Expected " . @{$expected_results->[0]} . " columns, got $cols\n"; return 0; } my $row_num = 0; diff --git a/compatibility/pg/php/pg_test.php b/compatibility/pg/php/pg_test.php index 7d30aca4..4e203b56 100644 --- a/compatibility/pg/php/pg_test.php +++ b/compatibility/pg/php/pg_test.php @@ -87,8 +87,12 @@ public function run($conn) { $st = $conn->prepare($this->query); $st->execute(); if ($st->columnCount() == 0) { + if (count($this->expectedResults) != 0) { + echo "Expected " . count($this->expectedResults) . " rows, got 0\n"; + return false; + } echo "Returns 0 rows\n"; - return count($this->expectedResults) == 0; + return true; } $rows = $st->fetchAll(PDO::FETCH_NUM); if (count($rows[0]) != count($this->expectedResults[0])) { diff --git a/compatibility/pg/python/pg_test.py b/compatibility/pg/python/pg_test.py index 771e0263..b1bebd75 100644 --- a/compatibility/pg/python/pg_test.py +++ b/compatibility/pg/python/pg_test.py @@ -10,8 +10,11 @@ def run(self, cursor): print(f"Running test: {self.query}") cursor.execute(self.query) if cursor.description is None: + if len(self.expected_results) != 0: + print(f"Expected {len(self.expected_results)} rows, got 0") + return False print("Returns 0 rows") - return len(self.expected_results) == 0 + return True rows = cursor.fetchall() if len(rows[0]) != len(self.expected_results[0]): @@ -32,7 +35,6 @@ def run(self, cursor): def __init__(self): self.conn = None - self.cursor = None self.tests = [] def connect(self, ip, port, user, password): @@ -44,12 +46,10 @@ def connect(self, ip, port, user, password): user=user, password=password ) - self.cursor = self.conn.cursor() except Exception as e: raise RuntimeError(e) def disconnect(self): - self.cursor.close() self.conn.close() def add_test(self, query, expected_results): @@ -57,16 +57,18 @@ def add_test(self, query, expected_results): def run_tests(self): for test in self.tests: + cursor = None try: self.conn.autocommit = False - if not test.run(self.cursor): - self.conn.rollback() + cursor = self.conn.cursor() + if not test.run(cursor): return False - self.conn.commit() except Exception as e: print(f"Error running test: {e}") - self.conn.rollback() return False + finally: + if cursor: + cursor.close() return True def read_tests_from_file(self, filename): diff --git a/compatibility/pg/r/PGTest.R b/compatibility/pg/r/PGTest.R new file mode 100644 index 00000000..b358e675 --- /dev/null +++ b/compatibility/pg/r/PGTest.R @@ -0,0 +1,114 @@ +library(RPostgres) + +Tests <- setRefClass( + "Tests", + fields = list( + conn = "ANY", + tests = "list" + ), + methods = list( + connect = function(ip, port, user, password) { + conn <<- dbConnect( + Postgres(), + host = ip, + port = port, + user = user, + password = password, + dbname = "postgres" + ) + }, + disconnect = function() { + dbDisconnect(conn) + }, + addTest = function(query, expectedResults) { + tests <<- c(tests, list(Test$new(query, expectedResults))) + }, + runTests = function() { + for (test in tests) { + if (!test$run(conn)) { + return(FALSE) + } + } + return(TRUE) + }, + readTestsFromFile = function(filename) { + lines <- readLines(filename) + i <- 1 + while (i <= length(lines)) { + if (trimws(lines[i]) == "") { + i <- i + 1 + next + } + query <- lines[i] + i <- i + 1 + results <- list() + while (i <= length(lines) && trimws(lines[i]) != "") { + results <- c(results, list(strsplit(lines[i], ",")[[1]])) + i <- i + 1 + } + addTest(query, results) + } + } + ) +) + +Test <- setRefClass( + "Test", + fields = list( + query = "character", + expectedResults = "list" + ), + methods = list( + initialize = function(query, expectedResults) { + query <<- query + expectedResults <<- expectedResults + }, + run = function(conn) { + cat("Running test:", query, "\n") + res <- dbSendQuery(conn, query) + fetched <- dbFetch(res) + dbClearResult(res) + if (length(expectedResults) == 0) { + if (nrow(fetched) == 0 || ncol(fetched) == 0) { + return(TRUE) + } else { + cat("Expected empty result, got", nrow(fetched), "rows and", ncol(fetched), "columns\n") + return(FALSE) + } + } + if (ncol(fetched) != length(expectedResults[[1]])) { + cat("Expected", length(expectedResults[[1]]), "columns, got", ncol(fetched), "\n") + return(FALSE) + } + if (nrow(fetched) != length(expectedResults)) { + cat("Expected", length(expectedResults), "rows, got", nrow(fetched), "\n") + return(FALSE) + } + for (i in seq_len(nrow(fetched))) { + for (j in seq_len(ncol(fetched))) { + if (as.character(fetched[i, j]) != expectedResults[[i]][j]) { + cat("Expected:", expectedResults[[i]][j], "Got:", fetched[i, j], "\n") + return(FALSE) + } + } + } + return(TRUE) + } + ) +) + +args <- commandArgs(trailingOnly = TRUE) +if (length(args) < 5) { + cat("Usage: Rscript PGTest.R \n") + quit(status = 1) +} + +tests <- Tests$new() +tests$connect(args[1], as.integer(args[2]), args[3], args[4]) +tests$readTestsFromFile(args[5]) + +if (!tests$runTests()) { + tests$disconnect() + quit(status = 1) +} +tests$disconnect() \ No newline at end of file diff --git a/compatibility/pg/ruby/pg_test.rb b/compatibility/pg/ruby/pg_test.rb index 0f864bd5..076816fa 100644 --- a/compatibility/pg/ruby/pg_test.rb +++ b/compatibility/pg/ruby/pg_test.rb @@ -70,8 +70,12 @@ def run(conn) puts "Running test: #{@query}" result = conn.exec(@query) if result.ntuples == 0 - puts "Returns 0 rows" - return @expected_results.empty? + if @expected_results.empty? + puts "Returns 0 rows" + return true + end + puts "Expected #{@expected_results.length} rows, got 0" + return false end if result.nfields != @expected_results[0].length puts "Expected #{@expected_results[0].length} columns, got #{result.nfields}" diff --git a/compatibility/pg/rust/Cargo.toml b/compatibility/pg/rust/Cargo.toml new file mode 100644 index 00000000..301b0027 --- /dev/null +++ b/compatibility/pg/rust/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "pg_test" + +[dependencies] +postgres = "*" + +[[bin]] +name = "pg_test" +path = "./pg_test.rs" \ No newline at end of file diff --git a/compatibility/pg/rust/pg_test.rs b/compatibility/pg/rust/pg_test.rs new file mode 100644 index 00000000..5eac933c --- /dev/null +++ b/compatibility/pg/rust/pg_test.rs @@ -0,0 +1,128 @@ +use std::fs::File; +use std::io::{self, BufRead, BufReader}; +use std::process::exit; + +extern crate postgres; +use postgres::{Client, NoTls, Error}; + +struct Test { + query: String, + expected_results: Vec>, +} + +impl Test { + fn new(query: String, expected_results: Vec>) -> Self { + Test { query, expected_results } + } + + fn run(&self, client: &mut Client) -> bool { + println!("Running test: {}", self.query); + match client.query(&self.query, &[]) { + Ok(rows) => { + if rows.is_empty() { + if self.expected_results.is_empty() { + println!("Returns 0 rows"); + return true; + } + eprintln!("Expected {} rows, got 0", self.expected_results.len()); + return false; + } + if rows[0].columns().len() != self.expected_results[0].len() { + eprintln!("Expected {} columns, got {}", self.expected_results[0].len(), rows[0].columns().len()); + return false; + } + for (i, row) in rows.iter().enumerate() { + for (j, expected) in self.expected_results[i].iter().enumerate() { + let result: String = row.get(j); + if expected != &result { + eprintln!("Expected:\n'{}'", expected); + eprintln!("Result:\n'{}'\nRest of the results:", result); + for row in rows.iter().skip(i + 1) { + eprintln!("{}", row.get::(0)); + } + return false; + } + } + } + println!("Returns {} rows", rows.len()); + if rows.len() != self.expected_results.len() { + eprintln!("Expected {} rows", self.expected_results.len()); + return false; + } + true + } + Err(err) => { + eprintln!("{}", err); + false + } + } + } +} + +struct Tests { + client: Client, + tests: Vec, +} + +impl Tests { + fn new(ip: &str, port: u16, user: &str, password: &str) -> Result { + let conn_str = format!("host={} port={} user={} password={} dbname=postgres", ip, port, user, password); + let client = Client::connect(&conn_str, NoTls)?; + Ok(Tests { client, tests: Vec::new() }) + } + + fn add_test(&mut self, query: String, expected_results: Vec>) { + self.tests.push(Test::new(query, expected_results)); + } + + fn run_tests(&mut self) -> bool { + for test in &self.tests { + if !test.run(&mut self.client) { + return false; + } + } + true + } + + fn read_tests_from_file(&mut self, filename: &str) -> io::Result<()> { + let file = File::open(filename)?; + let reader = BufReader::new(file); + let mut lines = reader.lines(); + while let Some(Ok(line)) = lines.next() { + if line.trim().is_empty() { + continue; + } + let query = line; + let mut results = Vec::new(); + while let Some(Ok(line)) = lines.next() { + if line.trim().is_empty() { + break; + } + results.push(line.split(',').map(String::from).collect()); + } + self.add_test(query, results); + } + Ok(()) + } +} + +fn main() { + let args: Vec = std::env::args().collect(); + if args.len() < 6 { + eprintln!("Usage: {} ", args[0]); + exit(1); + } + + let ip = &args[1]; + let port: u16 = args[2].parse().expect("Invalid port number"); + let user = &args[3]; + let password = &args[4]; + let test_file = &args[5]; + + let mut tests = Tests::new(ip, port, user, password).expect("Failed to connect to database"); + tests.read_tests_from_file(test_file).expect("Failed to read test file"); + + if !tests.run_tests() { + exit(1); + } +} \ No newline at end of file diff --git a/compatibility/pg/test.bats b/compatibility/pg/test.bats index caa88e2c..bab3858b 100644 --- a/compatibility/pg/test.bats +++ b/compatibility/pg/test.bats @@ -2,20 +2,33 @@ setup() { psql -h 127.0.0.1 -p 5432 -U postgres -c "DROP SCHEMA IF EXISTS test CASCADE;" + touch /tmp/test_pids } -@test "pg-c" { - gcc -o $BATS_TEST_DIRNAME/c/pg_test $BATS_TEST_DIRNAME/c/pg_test.c -I/usr/include/postgresql -lpq - run $BATS_TEST_DIRNAME/c/pg_test 127.0.0.1 5432 postgres "" $BATS_TEST_DIRNAME/test.data - if [ "$status" -ne 0 ]; then - echo "$output" +custom_teardown="" + +set_custom_teardown() { + custom_teardown="$1" +} + +teardown() { + if [ -n "$custom_teardown" ]; then + eval "$custom_teardown" + custom_teardown="" fi - [ "$status" -eq 0 ] + + while read -r pid; do + if kill -0 "$pid" 2>/dev/null; then + kill "$pid" + wait "$pid" 2>/dev/null + fi + done < /tmp/test_pids + rm /tmp/test_pids } -@test "pg-java" { - javac $BATS_TEST_DIRNAME/java/PGTest.java - run java -cp $BATS_TEST_DIRNAME/java:$BATS_TEST_DIRNAME/java/postgresql-42.7.4.jar PGTest 127.0.0.1 5432 postgres "" $BATS_TEST_DIRNAME/test.data +start_process() { + run timeout 2m "$@" + echo $! >> /tmp/test_pids if [ "$status" -ne 0 ]; then echo "$output" echo "$stderr" @@ -23,47 +36,52 @@ setup() { [ "$status" -eq 0 ] } +@test "pg-c" { + start_process gcc -o $BATS_TEST_DIRNAME/c/pg_test $BATS_TEST_DIRNAME/c/pg_test.c -I/usr/include/postgresql -lpq + start_process $BATS_TEST_DIRNAME/c/pg_test 127.0.0.1 5432 postgres "" $BATS_TEST_DIRNAME/test.data +} + +@test "pg-csharp" { + set_custom_teardown "sudo pkill -f dotnet" + start_process dotnet build $BATS_TEST_DIRNAME/csharp/PGTest.csproj -o $BATS_TEST_DIRNAME/csharp/bin + start_process dotnet $BATS_TEST_DIRNAME/csharp/bin/PGTest.dll 127.0.0.1 5432 postgres "" $BATS_TEST_DIRNAME/test.data +} + +@test "pg-go" { + start_process go build -o $BATS_TEST_DIRNAME/go/pg $BATS_TEST_DIRNAME/go/pg.go + start_process $BATS_TEST_DIRNAME/go/pg 127.0.0.1 5432 postgres "" $BATS_TEST_DIRNAME/test.data +} + +@test "pg-java" { + start_process javac $BATS_TEST_DIRNAME/java/PGTest.java + start_process java -cp $BATS_TEST_DIRNAME/java:$BATS_TEST_DIRNAME/java/postgresql-42.7.4.jar PGTest 127.0.0.1 5432 postgres "" $BATS_TEST_DIRNAME/test.data +} + @test "pg-node" { - run node $BATS_TEST_DIRNAME/node/pg_test.js 127.0.0.1 5432 postgres "" $BATS_TEST_DIRNAME/test.data - if [ "$status" -ne 0 ]; then - echo "$output" - echo "$stderr" - fi - [ "$status" -eq 0 ] + start_process node $BATS_TEST_DIRNAME/node/pg_test.js 127.0.0.1 5432 postgres "" $BATS_TEST_DIRNAME/test.data } @test "pg-perl" { - run perl $BATS_TEST_DIRNAME/perl/pg_test.pl 127.0.0.1 5432 postgres "" $BATS_TEST_DIRNAME/test.data - if [ "$status" -ne 0 ]; then - echo "$output" - echo "$stderr" - fi - [ "$status" -eq 0 ] + start_process perl $BATS_TEST_DIRNAME/perl/pg_test.pl 127.0.0.1 5432 postgres "" $BATS_TEST_DIRNAME/test.data } @test "pg-php" { - run php $BATS_TEST_DIRNAME/php/pg_test.php 127.0.0.1 5432 postgres "" $BATS_TEST_DIRNAME/test.data - if [ "$status" -ne 0 ]; then - echo "$output" - echo "$stderr" - fi - [ "$status" -eq 0 ] + start_process php $BATS_TEST_DIRNAME/php/pg_test.php 127.0.0.1 5432 postgres "" $BATS_TEST_DIRNAME/test.data } @test "pg-python" { - run python3 $BATS_TEST_DIRNAME/python/pg_test.py 127.0.0.1 5432 postgres "" $BATS_TEST_DIRNAME/test.data - if [ "$status" -ne 0 ]; then - echo "$output" - echo "$stderr" - fi - [ "$status" -eq 0 ] + start_process python3 $BATS_TEST_DIRNAME/python/pg_test.py 127.0.0.1 5432 postgres "" $BATS_TEST_DIRNAME/test.data +} + +@test "pg-r" { + start_process Rscript $BATS_TEST_DIRNAME/r/PGTest.R 127.0.0.1 5432 postgres "" $BATS_TEST_DIRNAME/test.data } @test "pg-ruby" { - run ruby $BATS_TEST_DIRNAME/ruby/pg_test.rb 127.0.0.1 5432 postgres "" $BATS_TEST_DIRNAME/test.data - if [ "$status" -ne 0 ]; then - echo "$output" - echo "$stderr" - fi - [ "$status" -eq 0 ] + start_process ruby $BATS_TEST_DIRNAME/ruby/pg_test.rb 127.0.0.1 5432 postgres "" $BATS_TEST_DIRNAME/test.data } + +@test "pg-rust" { + start_process cargo build --release --manifest-path $BATS_TEST_DIRNAME/rust/Cargo.toml + start_process $BATS_TEST_DIRNAME/rust/target/release/pg_test 127.0.0.1 5432 postgres "" $BATS_TEST_DIRNAME/test.data +} \ No newline at end of file diff --git a/compatibility/pg/test.data b/compatibility/pg/test.data index 122b2010..d14f2509 100644 --- a/compatibility/pg/test.data +++ b/compatibility/pg/test.data @@ -24,4 +24,4 @@ DELETE FROM test.tb1 WHERE id=1 SELECT * FROM test.tb1 2,2.2,b -DROP SCHEMA test CASCADE \ No newline at end of file +DROP SCHEMA test CASCADE