h2gis 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of h2gis might be problematic. Click here for more details.
- h2gis-0.0.1/MANIFEST.in +4 -0
- h2gis-0.0.1/PKG-INFO +32 -0
- h2gis-0.0.1/README.md +9 -0
- h2gis-0.0.1/docs/examples.md +46 -0
- h2gis-0.0.1/docs/h2gisconnector.md +35 -0
- h2gis-0.0.1/docs/index.md +9 -0
- h2gis-0.0.1/docs/installation.md +33 -0
- h2gis-0.0.1/h2gis/__init__.py +4 -0
- h2gis-0.0.1/h2gis/h2gisconnector.py +170 -0
- h2gis-0.0.1/h2gis/lib/h2gis.dll +0 -0
- h2gis-0.0.1/h2gis/lib/h2gis.so +0 -0
- h2gis-0.0.1/h2gis.egg-info/PKG-INFO +32 -0
- h2gis-0.0.1/h2gis.egg-info/SOURCES.txt +16 -0
- h2gis-0.0.1/h2gis.egg-info/dependency_links.txt +1 -0
- h2gis-0.0.1/h2gis.egg-info/top_level.txt +1 -0
- h2gis-0.0.1/setup.cfg +4 -0
- h2gis-0.0.1/setup.py +27 -0
- h2gis-0.0.1/test/test_h2gis.py +146 -0
h2gis-0.0.1/MANIFEST.in
ADDED
h2gis-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: h2gis
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A Python library to use an H2GIS database through a native GraalVM bridge.
|
|
5
|
+
Home-page: https://github.com/yourusername/h2gis
|
|
6
|
+
Author: Lemap
|
|
7
|
+
Author-email: you@example.com
|
|
8
|
+
License: LGPL-3.0
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.7
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Dynamic: author
|
|
15
|
+
Dynamic: author-email
|
|
16
|
+
Dynamic: classifier
|
|
17
|
+
Dynamic: description
|
|
18
|
+
Dynamic: description-content-type
|
|
19
|
+
Dynamic: home-page
|
|
20
|
+
Dynamic: license
|
|
21
|
+
Dynamic: requires-python
|
|
22
|
+
Dynamic: summary
|
|
23
|
+
|
|
24
|
+
# h2gis-python
|
|
25
|
+
|
|
26
|
+
A lib thal allows to interract with a h2gis lib. Do not forget when you create an H2gisConnection object to put the lib.so full path as a parameter. Without this, it will not work.
|
|
27
|
+
|
|
28
|
+
To insall run :
|
|
29
|
+
```
|
|
30
|
+
python setup.py bdist_wheel
|
|
31
|
+
pip install dist/h2gis-0.0.1-py3-none-any.whl --force-reinstall
|
|
32
|
+
```
|
h2gis-0.0.1/README.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# h2gis-python
|
|
2
|
+
|
|
3
|
+
A lib thal allows to interract with a h2gis lib. Do not forget when you create an H2gisConnection object to put the lib.so full path as a parameter. Without this, it will not work.
|
|
4
|
+
|
|
5
|
+
To insall run :
|
|
6
|
+
```
|
|
7
|
+
python setup.py bdist_wheel
|
|
8
|
+
pip install dist/h2gis-0.0.1-py3-none-any.whl --force-reinstall
|
|
9
|
+
```
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Usage Examples
|
|
2
|
+
|
|
3
|
+
## Basic Example
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
# Example usage:
|
|
7
|
+
from h2gis import H2gisConnector
|
|
8
|
+
import time
|
|
9
|
+
import statistics
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
h2gis = H2gisConnector("./h2gis/h2gis.so")
|
|
13
|
+
h2gis.connect("./test", "sa", "sa")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
#currently the two linew below are not working, you must do it via the .jar of h2gis, otherwise nothing will work
|
|
17
|
+
h2gis.execute("CREATE ALIAS IF NOT EXISTS H2GIS_SPATIAL FOR \"org.h2gis.functions.factory.H2GISFunctions.load\";")
|
|
18
|
+
|
|
19
|
+
h2gis.execute("DROP TABLE TEST IF EXISTS;")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
times_nat = []
|
|
23
|
+
i = 0
|
|
24
|
+
for i in range(0, 50):
|
|
25
|
+
start = time.perf_counter()
|
|
26
|
+
h2gis.execute("DROP TABLE TEST IF EXISTS;")
|
|
27
|
+
h2gis.execute("CALL GeoJsonRead('./test.geojson');")
|
|
28
|
+
end = time.perf_counter()
|
|
29
|
+
times_nat.append(end - start)
|
|
30
|
+
print("etape ", i, "finished")
|
|
31
|
+
|
|
32
|
+
print("elapsed native time ", sum(times_nat))
|
|
33
|
+
|
|
34
|
+
print("average native time ", statistics.mean(times_nat))
|
|
35
|
+
|
|
36
|
+
h2gis.close()
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Custom Native Library Path
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
db = H2gisConnector(lib_path="/custom/path/h2gis.so")
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Notes
|
|
46
|
+
- By default, the native `.so`, `.dll` are already included in the package, in the `lib` folder.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# H2gisConnector Class
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
`H2gisConnector` is a Python class that wraps a native shared library compiled with GraalVM. It provides access to an embedded H2GIS database using GraalVM isolate and thread objects.
|
|
6
|
+
|
|
7
|
+
## Constructor
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
H2gisConnector(lib_path=None)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
- `lib_path`: Optional path to the `.so`, `.dll`, or `.dylib` file. If not provided, a default path is used based on the current OS.
|
|
14
|
+
|
|
15
|
+
## Methods
|
|
16
|
+
|
|
17
|
+
### `connect(db_file: str, username="sa", password="")`
|
|
18
|
+
|
|
19
|
+
Connect to a local H2GIS database file.
|
|
20
|
+
|
|
21
|
+
### `execute(sql: str) -> int`
|
|
22
|
+
|
|
23
|
+
Execute an `INSERT`, `UPDATE`, or `DELETE` SQL statement.
|
|
24
|
+
|
|
25
|
+
### `fetch(sql: str) -> list[str]`
|
|
26
|
+
|
|
27
|
+
Execute a `SELECT` SQL query and return results as a list of strings (one per row).
|
|
28
|
+
|
|
29
|
+
### `close()`
|
|
30
|
+
|
|
31
|
+
Close the active database connection.
|
|
32
|
+
|
|
33
|
+
### `__del__()`
|
|
34
|
+
|
|
35
|
+
Destructor that automatically tears down the GraalVM isolate and closes any connection.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# H2GIS Python Package
|
|
2
|
+
|
|
3
|
+
This package provides a Python interface to the native H2GIS spatial database engine using a GraalVM-compiled native library and a C interface.
|
|
4
|
+
|
|
5
|
+
## Modules
|
|
6
|
+
|
|
7
|
+
- [connector.md](./connector.md): Detailed documentation for the `H2gisConnector` class.
|
|
8
|
+
- [usage.md](./usage.md): Example usage of the library.
|
|
9
|
+
- [installation.md](installation.md): Installation and setup instructions.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Installation
|
|
2
|
+
|
|
3
|
+
## Requirements
|
|
4
|
+
|
|
5
|
+
- Python 3.7+
|
|
6
|
+
- Native H2GIS shared library compiled with GraalVM
|
|
7
|
+
- Supported platforms: Linux, Windows, macOS
|
|
8
|
+
|
|
9
|
+
## Install the Python Package
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install .
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Ensure the native library file is present in the following structure:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
h2gis/
|
|
19
|
+
├── lib/
|
|
20
|
+
│ ├── h2gis.so # Linux
|
|
21
|
+
│ ├── h2gis.dylib # macOS
|
|
22
|
+
│ └── h2gis.dll # Windows
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Custom Library Path
|
|
26
|
+
|
|
27
|
+
You can specify a custom path to the native library:
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from h2gis import H2gisConnector
|
|
31
|
+
|
|
32
|
+
db = H2gisConnector(lib_path="/custom/path/to/h2gis.so")
|
|
33
|
+
```
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
import os
|
|
3
|
+
import platform
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# --- Opaque struct placeholders for GraalVM native API ---
|
|
7
|
+
class GraalIsolate(ctypes.Structure):
|
|
8
|
+
_fields_ = []
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class GraalIsolateThread(ctypes.Structure):
|
|
12
|
+
_fields_ = []
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class GraalIsolateParams(ctypes.Structure):
|
|
16
|
+
_fields_ = []
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
GraalIsolate_p = ctypes.POINTER(GraalIsolate)
|
|
20
|
+
GraalIsolateThread_p = ctypes.POINTER(GraalIsolateThread)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class H2gisConnector:
|
|
24
|
+
"""
|
|
25
|
+
Python wrapper for the native H2GIS library compiled with GraalVM.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, lib_path=None):
|
|
29
|
+
"""
|
|
30
|
+
Initialize the H2GIS connector and create a GraalVM isolate.
|
|
31
|
+
|
|
32
|
+
:param lib_path: Optional path to the native .so/.dll/.dylib library
|
|
33
|
+
"""
|
|
34
|
+
if lib_path is None:
|
|
35
|
+
lib_path = self._default_library_path()
|
|
36
|
+
|
|
37
|
+
self.lib = ctypes.CDLL(lib_path)
|
|
38
|
+
self._setup_c_function_signatures()
|
|
39
|
+
|
|
40
|
+
self.isolate = GraalIsolate_p()
|
|
41
|
+
self.thread = GraalIsolateThread_p()
|
|
42
|
+
self.connection = 0
|
|
43
|
+
|
|
44
|
+
params = GraalIsolateParams()
|
|
45
|
+
result = self.lib.graal_create_isolate(
|
|
46
|
+
ctypes.byref(params),
|
|
47
|
+
ctypes.byref(self.isolate),
|
|
48
|
+
ctypes.byref(self.thread)
|
|
49
|
+
)
|
|
50
|
+
if result != 0:
|
|
51
|
+
raise RuntimeError(f"Failed to create GraalVM isolate (code {result})")
|
|
52
|
+
|
|
53
|
+
def _default_library_path(self):
|
|
54
|
+
"""Find default library path relative to this file."""
|
|
55
|
+
base_dir = os.path.dirname(__file__)
|
|
56
|
+
system = platform.system()
|
|
57
|
+
if system == "Windows":
|
|
58
|
+
libname = "h2gis.dll"
|
|
59
|
+
else:
|
|
60
|
+
libname = "h2gis.so"
|
|
61
|
+
|
|
62
|
+
return os.path.join(base_dir, "lib", libname)
|
|
63
|
+
|
|
64
|
+
def _setup_c_function_signatures(self):
|
|
65
|
+
"""Declare argument and return types for native functions."""
|
|
66
|
+
self.lib.graal_create_isolate.argtypes = [
|
|
67
|
+
ctypes.POINTER(GraalIsolateParams),
|
|
68
|
+
ctypes.POINTER(GraalIsolate_p),
|
|
69
|
+
ctypes.POINTER(GraalIsolateThread_p),
|
|
70
|
+
]
|
|
71
|
+
self.lib.graal_create_isolate.restype = ctypes.c_int
|
|
72
|
+
|
|
73
|
+
self.lib.graal_tear_down_isolate.argtypes = [GraalIsolateThread_p]
|
|
74
|
+
self.lib.graal_tear_down_isolate.restype = ctypes.c_int
|
|
75
|
+
|
|
76
|
+
self.lib.h2gis_connect.argtypes = [
|
|
77
|
+
GraalIsolateThread_p,
|
|
78
|
+
ctypes.c_char_p,
|
|
79
|
+
ctypes.c_char_p,
|
|
80
|
+
ctypes.c_char_p,
|
|
81
|
+
]
|
|
82
|
+
self.lib.h2gis_connect.restype = ctypes.c_long
|
|
83
|
+
|
|
84
|
+
self.lib.h2gis_execute_update.argtypes = [
|
|
85
|
+
GraalIsolateThread_p,
|
|
86
|
+
ctypes.c_long,
|
|
87
|
+
ctypes.c_char_p,
|
|
88
|
+
]
|
|
89
|
+
self.lib.h2gis_execute_update.restype = ctypes.c_int
|
|
90
|
+
|
|
91
|
+
self.lib.h2gis_execute.argtypes = [
|
|
92
|
+
GraalIsolateThread_p,
|
|
93
|
+
ctypes.c_long,
|
|
94
|
+
ctypes.c_char_p,
|
|
95
|
+
]
|
|
96
|
+
self.lib.h2gis_execute.restype = ctypes.c_long
|
|
97
|
+
|
|
98
|
+
self.lib.h2gis_fetch_row.argtypes = [
|
|
99
|
+
GraalIsolateThread_p,
|
|
100
|
+
ctypes.c_long,
|
|
101
|
+
]
|
|
102
|
+
self.lib.h2gis_fetch_row.restype = ctypes.c_char_p
|
|
103
|
+
|
|
104
|
+
self.lib.h2gis_close_query.argtypes = [
|
|
105
|
+
GraalIsolateThread_p,
|
|
106
|
+
ctypes.c_long,
|
|
107
|
+
]
|
|
108
|
+
self.lib.h2gis_close_query.restype = None
|
|
109
|
+
|
|
110
|
+
self.lib.h2gis_close_connection.argtypes = [
|
|
111
|
+
GraalIsolateThread_p,
|
|
112
|
+
ctypes.c_long,
|
|
113
|
+
]
|
|
114
|
+
self.lib.h2gis_close_connection.restype = None
|
|
115
|
+
|
|
116
|
+
def connect(self, db_file: str, username="sa", password=""):
|
|
117
|
+
"""Connect to a H2GIS database file."""
|
|
118
|
+
self.connection = self.lib.h2gis_connect(
|
|
119
|
+
self.thread,
|
|
120
|
+
db_file.encode("utf-8"),
|
|
121
|
+
username.encode("utf-8"),
|
|
122
|
+
password.encode("utf-8"),
|
|
123
|
+
)
|
|
124
|
+
if self.connection == 0:
|
|
125
|
+
raise RuntimeError("Failed to connect to H2GIS database.")
|
|
126
|
+
|
|
127
|
+
def execute(self, sql: str) -> int:
|
|
128
|
+
"""Execute INSERT/UPDATE/DELETE statement and return affected row count."""
|
|
129
|
+
return self.lib.h2gis_execute_update(
|
|
130
|
+
self.thread,
|
|
131
|
+
self.connection,
|
|
132
|
+
sql.encode("utf-8"),
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
def fetch(self, sql: str) -> list[str]:
|
|
136
|
+
"""Execute SELECT statement and return list of rows."""
|
|
137
|
+
handle = self.lib.h2gis_execute(
|
|
138
|
+
self.thread,
|
|
139
|
+
self.connection,
|
|
140
|
+
sql.encode("utf-8"),
|
|
141
|
+
)
|
|
142
|
+
if handle == 0:
|
|
143
|
+
raise RuntimeError("Query execution failed")
|
|
144
|
+
|
|
145
|
+
rows = []
|
|
146
|
+
while True:
|
|
147
|
+
row = self.lib.h2gis_fetch_row(self.thread, handle)
|
|
148
|
+
if not row:
|
|
149
|
+
break
|
|
150
|
+
rows.append(row.decode("utf-8"))
|
|
151
|
+
|
|
152
|
+
self.lib.h2gis_close_query(self.thread, handle)
|
|
153
|
+
return rows
|
|
154
|
+
|
|
155
|
+
def close(self):
|
|
156
|
+
"""Close the database connection."""
|
|
157
|
+
if self.connection:
|
|
158
|
+
self.lib.h2gis_close_connection(self.thread, self.connection)
|
|
159
|
+
self.connection = 0
|
|
160
|
+
|
|
161
|
+
def __del__(self):
|
|
162
|
+
"""Destructor to clean up isolate and resources."""
|
|
163
|
+
try:
|
|
164
|
+
self.close()
|
|
165
|
+
except Exception:
|
|
166
|
+
pass
|
|
167
|
+
try:
|
|
168
|
+
self.lib.graal_tear_down_isolate(self.thread)
|
|
169
|
+
except Exception:
|
|
170
|
+
pass
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: h2gis
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A Python library to use an H2GIS database through a native GraalVM bridge.
|
|
5
|
+
Home-page: https://github.com/yourusername/h2gis
|
|
6
|
+
Author: Lemap
|
|
7
|
+
Author-email: you@example.com
|
|
8
|
+
License: LGPL-3.0
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.7
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Dynamic: author
|
|
15
|
+
Dynamic: author-email
|
|
16
|
+
Dynamic: classifier
|
|
17
|
+
Dynamic: description
|
|
18
|
+
Dynamic: description-content-type
|
|
19
|
+
Dynamic: home-page
|
|
20
|
+
Dynamic: license
|
|
21
|
+
Dynamic: requires-python
|
|
22
|
+
Dynamic: summary
|
|
23
|
+
|
|
24
|
+
# h2gis-python
|
|
25
|
+
|
|
26
|
+
A lib thal allows to interract with a h2gis lib. Do not forget when you create an H2gisConnection object to put the lib.so full path as a parameter. Without this, it will not work.
|
|
27
|
+
|
|
28
|
+
To insall run :
|
|
29
|
+
```
|
|
30
|
+
python setup.py bdist_wheel
|
|
31
|
+
pip install dist/h2gis-0.0.1-py3-none-any.whl --force-reinstall
|
|
32
|
+
```
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
MANIFEST.in
|
|
2
|
+
README.md
|
|
3
|
+
setup.py
|
|
4
|
+
docs/examples.md
|
|
5
|
+
docs/h2gisconnector.md
|
|
6
|
+
docs/index.md
|
|
7
|
+
docs/installation.md
|
|
8
|
+
h2gis/__init__.py
|
|
9
|
+
h2gis/h2gisconnector.py
|
|
10
|
+
h2gis.egg-info/PKG-INFO
|
|
11
|
+
h2gis.egg-info/SOURCES.txt
|
|
12
|
+
h2gis.egg-info/dependency_links.txt
|
|
13
|
+
h2gis.egg-info/top_level.txt
|
|
14
|
+
h2gis/lib/h2gis.dll
|
|
15
|
+
h2gis/lib/h2gis.so
|
|
16
|
+
test/test_h2gis.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
h2gis
|
h2gis-0.0.1/setup.cfg
ADDED
h2gis-0.0.1/setup.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from setuptools import find_packages, setup
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name='h2gis',
|
|
5
|
+
version='0.0.1',
|
|
6
|
+
description='A Python library to use an H2GIS database through a native GraalVM bridge.',
|
|
7
|
+
long_description=open("README.md").read(),
|
|
8
|
+
long_description_content_type="text/markdown",
|
|
9
|
+
author='Lemap',
|
|
10
|
+
author_email='you@example.com', # Optional, but recommended
|
|
11
|
+
url='https://github.com/yourusername/h2gis', # Replace with actual URL
|
|
12
|
+
packages=find_packages(),
|
|
13
|
+
include_package_data=True,
|
|
14
|
+
package_data={
|
|
15
|
+
"h2gis": ["lib/*.so", "lib/*.dll", "lib/*.dylib"],
|
|
16
|
+
"h2gis.docs": ["*.md"], # add this if docs is inside the h2gis package
|
|
17
|
+
},
|
|
18
|
+
license="LGPL-3.0",
|
|
19
|
+
classifiers=[
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
|
|
22
|
+
"Operating System :: OS Independent",
|
|
23
|
+
],
|
|
24
|
+
python_requires=">=3.7",
|
|
25
|
+
setup_requires=['pytest-runner'],
|
|
26
|
+
tests_require=['pytest==4.4.1'],
|
|
27
|
+
)
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from unittest.mock import MagicMock, patch
|
|
3
|
+
from h2gis.h2gisconnector import H2gisConnector
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@patch("h2gis.h2gisconnector.ctypes.CDLL")
|
|
7
|
+
class TestH2gis(unittest.TestCase):
|
|
8
|
+
def setUp(self):
|
|
9
|
+
"""
|
|
10
|
+
Set up mock native library with default return values.
|
|
11
|
+
This is run before each test.
|
|
12
|
+
"""
|
|
13
|
+
self.mock_lib = MagicMock()
|
|
14
|
+
self.mock_lib.graal_create_isolate.return_value = 0
|
|
15
|
+
self.mock_lib.h2gis_connect.return_value = 1234
|
|
16
|
+
self.mock_lib.h2gis_execute.return_value = 42
|
|
17
|
+
self.mock_lib.h2gis_fetch_row.side_effect = [b"row1", b"row2", None]
|
|
18
|
+
self.mock_lib.h2gis_execute_update.return_value = 1
|
|
19
|
+
self.mock_lib.graal_tear_down_isolate.return_value = 0
|
|
20
|
+
|
|
21
|
+
def _create_h2gis(self, mock_cdll):
|
|
22
|
+
"""
|
|
23
|
+
Helper method to create H2gis instance with mocked native lib.
|
|
24
|
+
|
|
25
|
+
@param mock_cdll: patched ctypes.CDLL
|
|
26
|
+
@return: instance of H2gis
|
|
27
|
+
"""
|
|
28
|
+
mock_cdll.return_value = self.mock_lib
|
|
29
|
+
return H2gisConnector(lib_path="fake.so")
|
|
30
|
+
|
|
31
|
+
# ------------------------
|
|
32
|
+
# ✅ SUCCESS TEST CASES
|
|
33
|
+
# ------------------------
|
|
34
|
+
|
|
35
|
+
def test_connect_success(self, mock_cdll):
|
|
36
|
+
"""
|
|
37
|
+
Test successful connection to database.
|
|
38
|
+
"""
|
|
39
|
+
h2gis = self._create_h2gis(mock_cdll)
|
|
40
|
+
h2gis.connect("/path/to/db")
|
|
41
|
+
self.assertEqual(h2gis.connection, 1234)
|
|
42
|
+
|
|
43
|
+
def test_execute_query_success(self, mock_cdll):
|
|
44
|
+
"""
|
|
45
|
+
Test successful SELECT query with multiple rows.
|
|
46
|
+
"""
|
|
47
|
+
h2gis = self._create_h2gis(mock_cdll)
|
|
48
|
+
h2gis.connection = 1234
|
|
49
|
+
result = h2gis.execute_query("SELECT * FROM my_table")
|
|
50
|
+
self.assertEqual(result, ["row1", "row2"])
|
|
51
|
+
self.mock_lib.h2gis_close_query.assert_called_once()
|
|
52
|
+
|
|
53
|
+
def test_execute_update_success(self, mock_cdll):
|
|
54
|
+
"""
|
|
55
|
+
Test successful UPDATE query with 1 row affected.
|
|
56
|
+
"""
|
|
57
|
+
h2gis = self._create_h2gis(mock_cdll)
|
|
58
|
+
h2gis.connection = 1234
|
|
59
|
+
affected = h2gis.execute_update("UPDATE my_table SET x = 1")
|
|
60
|
+
self.assertEqual(affected, 1)
|
|
61
|
+
|
|
62
|
+
def test_close_connection_success(self, mock_cdll):
|
|
63
|
+
"""
|
|
64
|
+
Test closing the connection resets the connection handle.
|
|
65
|
+
"""
|
|
66
|
+
h2gis = self._create_h2gis(mock_cdll)
|
|
67
|
+
h2gis.connection = 1234
|
|
68
|
+
h2gis.close()
|
|
69
|
+
self.assertEqual(h2gis.connection, 0)
|
|
70
|
+
self.mock_lib.h2gis_close_connection.assert_called_once()
|
|
71
|
+
|
|
72
|
+
# ------------------------
|
|
73
|
+
# ⚠️ LIMIT TEST CASES
|
|
74
|
+
# ------------------------
|
|
75
|
+
|
|
76
|
+
def test_execute_query_empty_result(self, mock_cdll):
|
|
77
|
+
"""
|
|
78
|
+
Test SELECT query returning no rows.
|
|
79
|
+
"""
|
|
80
|
+
self.mock_lib.h2gis_fetch_row.side_effect = [None]
|
|
81
|
+
h2gis = self._create_h2gis(mock_cdll)
|
|
82
|
+
h2gis.connection = 1234
|
|
83
|
+
result = h2gis.execute_query("SELECT * FROM empty_table")
|
|
84
|
+
self.assertEqual(result, [])
|
|
85
|
+
|
|
86
|
+
def test_execute_update_zero_affected(self, mock_cdll):
|
|
87
|
+
"""
|
|
88
|
+
Test UPDATE query with 0 rows affected.
|
|
89
|
+
"""
|
|
90
|
+
self.mock_lib.h2gis_execute_update.return_value = 0
|
|
91
|
+
h2gis = self._create_h2gis(mock_cdll)
|
|
92
|
+
h2gis.connection = 1234
|
|
93
|
+
affected = h2gis.execute_update("DELETE FROM my_table WHERE 1=0")
|
|
94
|
+
self.assertEqual(affected, 0)
|
|
95
|
+
|
|
96
|
+
def test_close_when_not_connected(self, mock_cdll):
|
|
97
|
+
"""
|
|
98
|
+
Test close() does nothing if already disconnected.
|
|
99
|
+
"""
|
|
100
|
+
h2gis = self._create_h2gis(mock_cdll)
|
|
101
|
+
h2gis.connection = 0
|
|
102
|
+
h2gis.close()
|
|
103
|
+
self.mock_lib.h2gis_close_connection.assert_not_called()
|
|
104
|
+
|
|
105
|
+
def test_del_implicit_cleanup(self, mock_cdll):
|
|
106
|
+
"""
|
|
107
|
+
Test that __del__ triggers close and cleanup.
|
|
108
|
+
"""
|
|
109
|
+
h2gis = self._create_h2gis(mock_cdll)
|
|
110
|
+
h2gis.connection = 1234
|
|
111
|
+
del h2gis # __del__ should be called without exception
|
|
112
|
+
|
|
113
|
+
# ------------------------
|
|
114
|
+
# ❌ ERROR TEST CASES
|
|
115
|
+
# ------------------------
|
|
116
|
+
|
|
117
|
+
def test_connect_failure(self, mock_cdll):
|
|
118
|
+
"""
|
|
119
|
+
Test exception when connection fails (native returns 0).
|
|
120
|
+
"""
|
|
121
|
+
self.mock_lib.h2gis_connect.return_value = 0
|
|
122
|
+
h2gis = self._create_h2gis(mock_cdll)
|
|
123
|
+
with self.assertRaises(RuntimeError) as ctx:
|
|
124
|
+
h2gis.connect("/bad/path")
|
|
125
|
+
self.assertIn("Failed to connect", str(ctx.exception))
|
|
126
|
+
|
|
127
|
+
def test_execute_query_failure(self, mock_cdll):
|
|
128
|
+
"""
|
|
129
|
+
Test exception if query execution fails (native returns 0).
|
|
130
|
+
"""
|
|
131
|
+
self.mock_lib.h2gis_execute.return_value = 0
|
|
132
|
+
h2gis = self._create_h2gis(mock_cdll)
|
|
133
|
+
h2gis.connection = 1234
|
|
134
|
+
with self.assertRaises(RuntimeError) as ctx:
|
|
135
|
+
h2gis.execute_query("SELECT * FROM bad_table")
|
|
136
|
+
self.assertIn("Query execution failed", str(ctx.exception))
|
|
137
|
+
|
|
138
|
+
def test_graal_create_isolate_failure(self, mock_cdll):
|
|
139
|
+
"""
|
|
140
|
+
Test exception if GraalVM isolate creation fails.
|
|
141
|
+
"""
|
|
142
|
+
self.mock_lib.graal_create_isolate.return_value = 1
|
|
143
|
+
mock_cdll.return_value = self.mock_lib
|
|
144
|
+
with self.assertRaises(RuntimeError) as ctx:
|
|
145
|
+
H2gisConnector(lib_path="fake.so")
|
|
146
|
+
self.assertIn("Failed to create GraalVM isolate", str(ctx.exception))
|