diff --git a/.github/workflows/flatpak.yml b/.github/workflows/flatpak.yml index 6175e95..3ace40a 100644 --- a/.github/workflows/flatpak.yml +++ b/.github/workflows/flatpak.yml @@ -1,6 +1,6 @@ on: push: - branches: [main] + branches: [main, develop] pull_request: name: Build flatpak jobs: @@ -8,12 +8,26 @@ jobs: name: "Flatpak" runs-on: ubuntu-latest container: - image: bilelmoussaoui/flatpak-github-actions:gnome-46 - options: --privileged + image: bilelmoussaoui/flatpak-github-actions:gnome-47 + options: >- + --privileged + services: + postgres: + image: postgres + env: + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + POSTGRES_DB: postgres + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 steps: - - uses: actions/checkout@v4 - - uses: flatpak/flatpak-github-actions/flatpak-builder@v6 - with: - bundle: io.github.ppvan.tarug.flatpak - manifest-path: pkgs/flatpak/io.github.ppvan.tarug.yml - cache-key: flatpak-builder-${{ github.sha }} + - uses: actions/checkout@v4 + - uses: flatpak/flatpak-github-actions/flatpak-builder@v6 + with: + bundle: io.github.ppvan.tarug.flatpak + manifest-path: pkgs/flatpak/io.github.ppvan.tarug.yml + cache-key: flatpak-builder-${{ github.sha }} + run-tests: true diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 7f7b32a..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "files.watcherExclude": { - "**/.git/objects/**": true, - "**/.git/subtree-cache/**": true, - "**/.hg/store/**": true, - ".flatpak/**": true, - "_build/**": true - }, - "C_Cpp.default.compileCommands": "/home/ppvan/Documents/code/github/tarug/_build/compile_commands.json", - "C_Cpp.default.configurationProvider": "mesonbuild.mesonbuild", - "vala.languageServerPath": "${workspaceFolder}/.flatpak/vala-language-server.sh", - "blueprint-gtk.command": "flatpak", - "blueprint-gtk.arguments": [ - "build", - ".flatpak/repo/", - "/app/bin/blueprint-compiler" - ], - "mesonbuild.configureOnOpen": false, - "mesonbuild.buildFolder": "_build", - "mesonbuild.mesonPath": "${workspaceFolder}/.flatpak/meson.sh" -} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index ba46a02..51dc160 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -4,19 +4,15 @@ "version": "2.0.0", "tasks": [ { - "label": "Compile", + "label": "Meson (Flatpak): Run Unit test", "type": "shell", - "command": "meson compile -C build", - "problemMatcher": [], - "group": { - "kind": "build", - "isDefault": true - } - }, - { - "label": "Setup", - "type": "shell", - "command": "meson setup build --reconfigure", + "options": { + // Match meson buildPath + "cwd": "${workspaceFolder}/_build" + }, + "group": "test", + // mesonbuild.mesonPath + "command": "${workspaceFolder}/.flatpak/meson.sh test", } ] } \ No newline at end of file diff --git a/meson.build b/meson.build index 651719e..3389717 100644 --- a/meson.build +++ b/meson.build @@ -20,6 +20,7 @@ valac = meson.get_compiler('vala') subdir('data') subdir('resources') subdir('src') +subdir('tests') subdir('po') # Using gnome module to do some task after the app is installed, like compile schema (settings data) diff --git a/pkgs/flatpak/io.github.ppvan.tarug.yml b/pkgs/flatpak/io.github.ppvan.tarug.yml index e9fa4be..dcedb3e 100644 --- a/pkgs/flatpak/io.github.ppvan.tarug.yml +++ b/pkgs/flatpak/io.github.ppvan.tarug.yml @@ -1,6 +1,6 @@ app-id: io.github.ppvan.tarug runtime: org.gnome.Platform -runtime-version: '47' +runtime-version: "47" sdk: org.gnome.Sdk sdk-extensions: @@ -26,11 +26,16 @@ cleanup: - /share/man - /share/pkgconfig - /share/vala - - '*.la' - - '*.a' + - "*.la" + - "*.a" modules: - name: tarug buildsystem: meson + build-options: + test-args: + - "--socket=x11" + - "--share=network" + - "--env=POSTGRES_HOST=postgres" sources: - type: dir path: ../../ @@ -42,7 +47,7 @@ modules: url: https://gitlab.gnome.org/jwestman/blueprint-compiler tag: v0.14.0 cleanup: - - '*' + - "*" - name: postgresql-libs buildsystem: simple build-commands: diff --git a/src/cmd.vala b/src/cmd.vala deleted file mode 100644 index 9d34c6c..0000000 --- a/src/cmd.vala +++ /dev/null @@ -1,49 +0,0 @@ -namespace Tarug { - - - public static void test_query_epoll() { - var loop = new MainLoop(); - - // Prepare dependency injecttion container. - var container = Container.instance(); - var settings = new Settings(Config.APP_ID); - container.register(settings); - - var sql_service = new SQLService(); - var conn = new Connection("test conn") { - host = "127.0.0.1", - port = "5432", - user = "jay_user", - password = "jay_password", - database = "dvdrental" - }; - - sql_service.connect_db.begin(conn, (obj, res) => { - var text = """SELECT ta.tablename, cls.reltuples::bigint AS estimate FROM pg_tables ta - JOIN pg_class cls ON cls.relname = ta.tablename - WHERE schemaname=$1;"""; - - var query = new Query.with_params(text, {"public"}); - sql_service.exec_query_params.begin(query, (obj, res) => { - var relation = (Relation) sql_service.exec_query_params.end(res); - sql_service.exec_query.begin(new Query("SELECT NOW()"), (obj, res) => { - var relation2 = (Relation) sql_service.exec_query.end(res); - printerr(relation.to_string()); - printerr(relation2.to_string()); - loop.quit(); - }); - - }); - }); - loop.run(); - } - - - - public static int main(string []args) { - Test.init(ref args); - Test.add_func("/sql-service/basic", test_query_epoll); - - return Test.run(); - } -} \ No newline at end of file diff --git a/src/meson.build b/src/meson.build index a4abdc8..44d89b3 100644 --- a/src/meson.build +++ b/src/meson.build @@ -146,18 +146,3 @@ executable( ], install: true, ) - - -tarug_sources_test = tarug_sources + files('cmd.vala') -test( - 'foo', - executable( - 'tarug_test', - [tarug_resources, tarug_sources_test], - dependencies: tarug_deps, - vala_args: [ - '--gresourcesdir=' + tarug_resources_dir, - ] - ), - protocol: 'tap', -) diff --git a/src/models/Connection.vala b/src/models/Connection.vala index ad916c7..3bb590b 100644 --- a/src/models/Connection.vala +++ b/src/models/Connection.vala @@ -90,8 +90,12 @@ namespace Tarug { public string connection_string (int connection_timeout, int query_timeout){ var ssl_mode = use_ssl ? "verify-full" : "disable"; var options = @"\'-c statement_timeout=$(query_timeout * 1000)\'"; + var res = Resolver.get_default (); + var adds = res.lookup_by_name (host, null); - var base_str = @"user=$user password=$password port=$port host=$host dbname=$database application_name=$(Config.APP_NAME) sslmode=$ssl_mode connect_timeout=$connection_timeout options=$options"; + var host_addr = adds.nth_data (0).to_string (); + + var base_str = @"user=$user password=$password port=$port hostaddr=$host_addr dbname=$database application_name=$(Config.APP_NAME) sslmode=$ssl_mode connect_timeout=$connection_timeout options=$options"; var builder = new StringBuilder(base_str); if (use_ssl) { diff --git a/src/services/SQLService.vala b/src/services/SQLService.vala index d31376c..6cb1874 100644 --- a/src/services/SQLService.vala +++ b/src/services/SQLService.vala @@ -107,6 +107,12 @@ namespace Tarug { } } + public void close_db() { + this.active_db = null; + this.active_chanel = null; + this.active_result = null; + } + private void start_connect(string db_url) throws TarugError { active_db = Postgres.connect_start (db_url); var status = active_db.get_status (); diff --git a/src/utils/logging.vala b/src/utils/logging.vala index 4347c4f..a52231e 100644 --- a/src/utils/logging.vala +++ b/src/utils/logging.vala @@ -4,9 +4,9 @@ namespace Tarug { var prog = GLib.Environment.get_prgname(); GLib.on_error_stack_trace(prog); - Log.set_handler(Config.G_LOG_DOMAIN, LogLevelFlags.LEVEL_DEBUG | LogLevelFlags.LEVEL_WARNING, log_function); switch (debug_domain) { case Config.G_LOG_DOMAIN, "all": + Log.set_handler(Config.G_LOG_DOMAIN, LogLevelFlags.LEVEL_DEBUG | LogLevelFlags.LEVEL_WARNING, log_function); break; default: diff --git a/tests/meson.build b/tests/meson.build new file mode 100644 index 0000000..af64e87 --- /dev/null +++ b/tests/meson.build @@ -0,0 +1,39 @@ +gnome = import('gnome') + +conf = configuration_data() + +conf.set_quoted('APP_ID', app_id) +conf.set_quoted('APP_NAME', 'Tarug') +conf.set_quoted('G_LOG_DOMAIN', 'Tarug') +conf.set_quoted('VERSION', meson.project_version()) +conf.set_quoted('GETTEXT_PACKAGE', meson.project_name()) +conf.set_quoted( + 'DATADIR', + join_paths(get_option('prefix'), get_option('datadir')), +) +conf.set_quoted( + 'GNOMELOCALEDIR', + join_paths(get_option('prefix'), get_option('localedir')), +) +configure_file(output: 'config.h', configuration: conf) + + +tests = [ + 'sql_connect', +] + +foreach name : tests + test( + name, + executable( + name + '_test', + [tarug_resources, tarug_sources, 'test_runtime.vala', name + '_test.vala'], + dependencies: tarug_deps, + vala_args: [ + '--gresourcesdir=' + tarug_resources_dir, + ] + ), + protocol: 'tap', + env: ['G_TEST_SRCDIR=' + meson.current_source_dir()] + ) +endforeach \ No newline at end of file diff --git a/tests/sql_connect_test.vala b/tests/sql_connect_test.vala new file mode 100644 index 0000000..b868adc --- /dev/null +++ b/tests/sql_connect_test.vala @@ -0,0 +1,64 @@ +using Tarug; + + +void test_connect_db_ok (){ + + var main_loop = GLib.MainContext.default(); + var waiter = new AsyncResultWaiter(main_loop); + var sql_service = new SQLService(); + var conn = new Connection("test conn") { + host = Environment.get_variable ("POSTGRES_HOST") ?? "127.0.0.1", + port = "5432", + user = "postgres", + password = "postgres", + database = "postgres" + }; + + + sql_service.connect_db.begin(conn, waiter.async_completion); + + try { + sql_service.connect_db.end(waiter.async_result()); + sql_service.close_db (); + } catch (TarugError err) { + Test.fail_printf(err.message); + } +} + +void test_connect_db_fail (){ + var main_loop = GLib.MainContext.default(); + var waiter = new AsyncResultWaiter(main_loop); + var sql_service = new SQLService(); + + var conn = new Connection("wrong database config") { + host = Environment.get_variable ("POSTGRES_HOST") ?? "127.0.0.1", + port = "5432", + user = "postgres", + password = "postgres", + database = "dogsarethebest" + }; + + + sql_service.connect_db.begin(conn, waiter.async_completion); + + try { + sql_service.connect_db.end(waiter.async_result()); + sql_service.close_db (); + } catch (TarugError err) { + printerr (err.message); + } +} + +public int main (string[] args){ + Test.init(ref args); + + var container = Container.instance(); + var settings = new Settings(Config.APP_ID); + container.register(settings); + + // Test.add_func("/database/connect_fail", test_connect_db_fail); + Test.add_func("/database/connect_success", test_connect_db_ok); + + + return Test.run(); +} \ No newline at end of file diff --git a/tests/test_runtime.vala b/tests/test_runtime.vala new file mode 100644 index 0000000..c9e50b1 --- /dev/null +++ b/tests/test_runtime.vala @@ -0,0 +1,69 @@ + + +// Reference: https://gitlab.gnome.org/partizan/geary +public class AsyncResultWaiter : GLib.Object { + /** The main loop that is executed when waiting for async results. */ + public GLib.MainContext main_loop { get; construct set; } + + private GLib.AsyncQueue results = + new GLib.AsyncQueue(); + + + /** + * Constructs a new waiter. + * + * @param main_loop a main loop context to execute when waiting + * for an async result + */ + public AsyncResultWaiter(GLib.MainContext main_loop) { + Object(main_loop: main_loop); + } + + /** + * The last argument of an async call to be tested. + * + * Records the given {@link GLib.AsyncResult}, adding it to the + * internal FIFO queue. This method should be called as the + * completion of an async call to be tested. + * + * To use it, pass as the last argument to the `begin()` form of + * the async call: + * + * {{{ + * var waiter = new AsyncResultWaiter(); + * my_async_call.begin("foo", waiter.async_completion); + * }}} + */ + public void async_completion(GLib.Object? object, + GLib.AsyncResult result) { + this.results.push(result); + // Notify the loop so that if async_result() has already been + // called, that method won't block. + this.main_loop.wakeup(); + } + + /** + * Waits for async calls to complete, returning the most recent one. + * + * This returns the first {@link GLib.AsyncResult} from the + * internal FIFO queue that has been provided by {@link + * async_completion}. If none are available, it will pump the main + * loop, blocking until one becomes available. + * + * To use it, pass its return value as the argument to the `end()` + * call: + * + * {{{ + * my_async_call.end(waiter.async_result()); + * }}} + */ + public GLib.AsyncResult async_result() { + GLib.AsyncResult? result = this.results.try_pop(); + while (result == null) { + this.main_loop.iteration(true); + result = this.results.try_pop(); + } + return (GLib.AsyncResult) result; + } + +}