From 9f386c39ea7bb534fa788be06eacd44156d5a34e Mon Sep 17 00:00:00 2001 From: Daniela Petruzalek Date: Mon, 15 Jul 2024 07:23:55 +0100 Subject: [PATCH] add database persistence --- Makefile | 2 +- README.md | 40 ++++++++++++++++++++++++++++++++- data.go | 9 ++++++++ main.go | 59 ++++++++++++++++++++++++++++++++++++------------- sql/queries.sql | 4 +++- sql/schema.sql | 5 ++++- 6 files changed, 100 insertions(+), 19 deletions(-) diff --git a/Makefile b/Makefile index 59ae21d..c2c1867 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ .PHONY: build build: - go build -o bin/tq + go build -o bin/tq -ldflags="-X 'main.Version=v0.1'" .PHONY: test test: build diff --git a/README.md b/README.md index 9dc2ec8..7e9fd97 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,45 @@ It is currently under development so it doesn't support a lot of information yet ## Usage -Disclaimer: this tool is currently a prototype that has been through no more than 48 hours of active development, so use it at your own risk. Ironically, I haven't written any tests for it `:)` +```sh +% tq --help +Usage of tq: + -dbfile string + database file name for use with --persist and --open (default "testquery.db") + -open + open a database from a previous run + -persist + persist database between runs + -pkg string + directory of the package to test (default ".") + -query string + runs a single query and returns the result + +``` +By default tq will launch in iterative mode unless you pass a `--query` flag: + +```sh +% tq --persist --open --query "select * from code_coverage where file = 'div.go'" ++--------+-------------+-----------------------------------------------------------+---------+ +| FILE | LINE_NUMBER | CONTENT | COVERED | ++--------+-------------+-----------------------------------------------------------+---------+ +| div.go | 1 | package testdata | 0 | +| div.go | 2 | | 0 | +| div.go | 3 | import "errors" | 0 | +| div.go | 4 | | 0 | +| div.go | 5 | var ErrDivideByZero = errors.New("cannot divide by zero") | 0 | +| div.go | 6 | | 0 | +| div.go | 7 | func divide(dividend, divisor int) (int, error) { | 1 | +| div.go | 8 | if divisor == 0 { | 1 | +| div.go | 9 | return 0, ErrDivideByZero | 1 | +| div.go | 10 | } | 1 | +| div.go | 11 | | 0 | +| div.go | 12 | return dividend / divisor, nil | 1 | +| div.go | 13 | } | 0 | +| div.go | 14 | | 0 | ++--------+-------------+-----------------------------------------------------------+---------+ +``` + To use it, compile the code with `make build` (or `go build` if you are being wild) and run `tq` from the command line. By default `tq` will run data collection on the current directory but you can pass a package to it by using the `--pkg` flag. diff --git a/data.go b/data.go index edf1655..1ab7583 100644 --- a/data.go +++ b/data.go @@ -39,3 +39,12 @@ func populateTables(ctx context.Context, db *sql.DB, pkgDir string) error { return nil } + +func persistDatabase(db *sql.DB, dbFile string) error { + _, err := db.Exec("VACUUM INTO ?", dbFile) + if err != nil { + return fmt.Errorf("failed to save database file: %w", err) + } + + return nil +} diff --git a/main.go b/main.go index 03fd812..54394c5 100644 --- a/main.go +++ b/main.go @@ -17,15 +17,27 @@ import ( "github.com/jedib0t/go-pretty/v6/table" ) +var Version = "dev" + func main() { pkgDir := flag.String("pkg", ".", "directory of the package to test") + persist := flag.Bool("persist", false, "persist database between runs") + dbFile := flag.String("dbfile", "testquery.db", "database file name for use with --persist and --open") + openDB := flag.Bool("open", false, "open a database from a previous run") + query := flag.String("query", "", "runs a single query and returns the result") + version := flag.Bool("version", false, "shows version information") flag.Parse() + if *version { + fmt.Println("tq", Version) + return + } + ctx := context.Background() rl, err := readline.NewEx(&readline.Config{ Prompt: "> ", - HistoryFile: "/tmp/readline-multiline", + HistoryFile: "/tmp/testquery-history", DisableAutoSaveHistory: true, }) if err != nil { @@ -33,30 +45,47 @@ func main() { } defer rl.Close() - err = run(ctx, *pkgDir, rl) + err = run(ctx, *pkgDir, rl, *persist, *openDB, *dbFile, *query) if err != nil && !errors.Is(err, io.EOF) && !errors.Is(err, context.Canceled) { log.Fatalln(err) } } -func run(ctx context.Context, pkgDir string, rl *readline.Instance) error { - // Initialize the in-memory SQLite database - db, err := sql.Open("sqlite3", ":memory:") - if err != nil { - return fmt.Errorf("failed to instantiate sqlite: %w", err) - } - defer db.Close() +func run(ctx context.Context, pkgDir string, rl *readline.Instance, persist, open bool, dbFile string, query string) error { + var db *sql.DB + var err error - err = createTables(ctx, db) - if err != nil { - return fmt.Errorf("failed to apply ddl: %w", err) + if open { + db, err = sql.Open("sqlite3", dbFile) + if err != nil { + return fmt.Errorf("failed to open database: %w", err) + } + defer db.Close() + } else { + db, err = sql.Open("sqlite3", ":memory:") + if err != nil { + return fmt.Errorf("failed to instantiate sqlite: %w", err) + } + defer db.Close() + + err = createTables(ctx, db) + if err != nil { + return fmt.Errorf("failed to apply ddl: %w", err) + } + + err = populateTables(ctx, db, pkgDir) + if err != nil { + return fmt.Errorf("failed to populate tables: %w", err) + } } - err = populateTables(ctx, db, pkgDir) - if err != nil { - return fmt.Errorf("failed to populate tables: %w", err) + if persist { + defer persistDatabase(db, dbFile) } + if query != "" { + return executeQuery(db, query) + } return prompt(ctx, db, rl) } diff --git a/sql/queries.sql b/sql/queries.sql index e91dac3..4605bd3 100644 --- a/sql/queries.sql +++ b/sql/queries.sql @@ -6,4 +6,6 @@ select function_name, file, start_line, end_line from missing_coverage; select test_name, function_name, start_line, end_line count from test_coverage where count > 0; select file, line_number, content from all_code limit 10; -select file, line_number, content from all_code where (file, line_number) in (select file, start_line from test_coverage where count = 0); \ No newline at end of file +select file, line_number, content from all_code where (file, line_number) in (select file, start_line from test_coverage where count = 0); + +select distinct ac.file, line_number, content, ifnull(count, 0) covered from all_code ac left join all_coverage cov on ac.file = cov.file and ac.line_number between cov.start_line and cov.end_line where ac.file not like '%_test.go'; \ No newline at end of file diff --git a/sql/schema.sql b/sql/schema.sql index 3fb0eb9..280573f 100644 --- a/sql/schema.sql +++ b/sql/schema.sql @@ -52,4 +52,7 @@ select package, test create view missing_coverage as select package, function_name, file, start_line, start_col, end_line, end_col from all_coverage - where count = 0; \ No newline at end of file + where count = 0; + + create view code_coverage as + select distinct ac.file, line_number, content, ifnull(count, 0) covered from all_code ac left join all_coverage cov on ac.file = cov.file and ac.line_number between cov.start_line and cov.end_line where ac.file not like '%_test.go'; \ No newline at end of file