Skip to content

Commit

Permalink
add some tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
ColinLeeo committed Feb 14, 2025
1 parent 33b1492 commit 24013da
Show file tree
Hide file tree
Showing 12 changed files with 254 additions and 73 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/unit-test-cpp-py.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ jobs:
- name: Build and test with Maven
shell: bash
run: |
./mvnw${{ steps.platform_suffix.outputs.platform_suffix }} -P with-cpp clean verify
./mvnw${{ steps.platform_suffix.outputs.platform_suffix }} -P with-cpp,with-python clean verify
- name: Upload whl Artifact
uses: actions/upload-artifact@v4
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ python/tsfile/*.h
python/tsfile/*.cpp
python/data
python/venv/*
python/tests/__pycache__/*
python/tests/*.tsfile
cpp/cmake-build-debug-mingw/

.vscode/
Expand Down
2 changes: 1 addition & 1 deletion python/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@
<configuration>
<executable>${python.venv.bin}pytest</executable>
<arguments>
<argument>${project.basedir}/test.py</argument>
<argument>${project.basedir}/tests</argument>
</arguments>
</configuration>
</execution>
Expand Down
2 changes: 1 addition & 1 deletion python/tests/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def test_tablet():

assert "test" == tablet.get_device_id()
assert 4 == len(tablet.get_column_name_list())
assert TSDataType.INT32 == tablet.get_type_list()[0]
assert TSDataType.INT32 == tablet.get_data_type_list()[0]

tablet.add_timestamp(0, 10)
tablet.add_value_by_name("temp1", 0, 10)
Expand Down
56 changes: 56 additions & 0 deletions python/tests/test_write.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
import os

import pytest

from tsfile import TsFileWriter, TimeseriesSchema, DeviceSchema
from tsfile import Tablet, RowRecord, Field
from tsfile import TSDataType

def test_row_record_write():
writer = TsFileWriter("record_write.tsfile")
timeseries = TimeseriesSchema("level1", TSDataType.INT64)
writer.register_timeseries("root.device1", timeseries)

record = RowRecord("root.device1", 10,[Field("level1", TSDataType.INT64, 10)])
writer.write_row_record(record)
if os.path.exists("record_write.tsfile"):
os.remove("record_write.tsfile")

def test_tablet_write():
if os.path.exists("tablet_write.tsfile"):
os.remove("tablet_write.tsfile")
writer = TsFileWriter("tablet_write.tsfile")
timeseries1 = TimeseriesSchema("level1", TSDataType.INT64)
timeseries2 = TimeseriesSchema("level2", TSDataType.DOUBLE)
device = DeviceSchema("root.device1", [timeseries1, timeseries2])
writer.register_device(device)

tablet = Tablet("root.device1", ["level1", "level2"], [TSDataType.INT64, TSDataType.DOUBLE])
for i in range(100):
tablet.add_timestamp(i, i)
tablet.add_value_by_index(0, i, i + 1)
tablet.add_value_by_name("level2", i, i * 0.1)

writer.write_tablet(tablet)
if os.path.exists("tablet_write.tsfile"):
os.remove("tablet_write.tsfile")



90 changes: 90 additions & 0 deletions python/tests/test_write_and_read.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#

import pytest

import os

from tsfile import TsFileWriter, TsFileReader
from tsfile import TimeseriesSchema, DeviceSchema
from tsfile import TSDataType
from tsfile import Tablet, RowRecord, Field

def test_row_record_write_and_read():
writer = TsFileWriter("record_write_and_read.tsfile")
timeseries = TimeseriesSchema("level1", TSDataType.INT64)
writer.register_timeseries("root.device1", timeseries)
writer.register_timeseries("root.device1", TimeseriesSchema("level2", TSDataType.DOUBLE))
writer.register_timeseries("root.device1", TimeseriesSchema("level3", TSDataType.INT32))

max_row_num = 1000
for i in range(max_row_num):
row = RowRecord("root.device1", i,
[Field("level1", TSDataType.INT64, i),
Field("level2", TSDataType.DOUBLE, i*1.1),
Field("level3", TSDataType.INT32, i*2)])
writer.write_row_record(row)

writer.close()

reader = TsFileReader("record_write_and_read.tsfile")
result = reader.query_timeseries("root.device1", ["level1","level2"], 10, 100)
i = 10
while result.has_next():
assert result.get_value_by_index(0) == i
assert result.get_value_by_name("level2") == i * 1.1
i = i + 1
reader.close()
if os.path.exists("record_write_and_read.tsfile"):
os.remove("record_write_and_read.tsfile")

def test_tablet_write_and_read():
writer = TsFileWriter("tablet_write_and_read.tsfile")
measurement_num = 30
for i in range(measurement_num):
writer.register_timeseries("root.device1", TimeseriesSchema('level' + str(i), TSDataType.INT64))

max_row_num = 10000
tablet_row_num = 1000
tablet_num = 0
for i in range(max_row_num // tablet_row_num):
tablet = Tablet("root.device1",[f'level{j}' for j in range(measurement_num)],[TSDataType.INT64 for _ in range(measurement_num)], tablet_row_num)
for row in range(tablet_row_num):
tablet.add_timestamp(row, row + tablet_num * tablet_row_num)
for col in range(measurement_num):
tablet.add_value_by_index(col, row, row + tablet_num * tablet_row_num)
writer.write_tablet(tablet)
tablet_num += 1

writer.close()

reader = TsFileReader("tablet_write_and_read.tsfile")
result = reader.query_timeseries("root.device1", ["level0"], 0, 1000000)
row_num = 0
while result.has_next():
assert result.is_null_by_index(0) == False
assert result.get_value_by_name("level0") == row_num
row_num = row_num + 1

assert row_num == max_row_num
reader.close()

if os.path.exists("tablet_write_and_read.tsfile"):
os.remove("tablet_write_and_read.tsfile")


99 changes: 53 additions & 46 deletions python/tsfile/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,93 +20,95 @@ class LibraryError(Exception):
_default_message = "Unknown error occurred"
_default_code = -1

def __init__(self, code=None, message=None):
self.code = code
self.message = message or self._default_message
def __init__(self, code=None, context=None):
self.code = code if code is not None else self._default_code
self.message = context if context is not None else self._default_message
super().__init__(f"[{code}] {self.message}")
def __str__(self):
return f"{self.code}: {self.message}"

class OOMError(LibraryError):
default_message = "Out of memory"
default_code = 1
_default_message = "Out of memory"
_default_code = 1

class NotExistsError(LibraryError):
default_message = "Requested resource does not exist"
default_code = 2
_default_message = "Requested resource does not exist"
_default_code = 2

class AlreadyExistsError(LibraryError):
default_message = "Resource already exists"
default_code = 3
_default_message = "Resource already exists"
_default_code = 3

# 参数相关异常
class InvalidArgumentError(LibraryError):
default_message = "Invalid argument provided"
default_code = 4
_default_message = "Invalid argument provided"
_default_code = 4

class OutOfRangeError(LibraryError):
default_message = "Value out of valid range"
default_code = 5
_default_message = "Value out of valid range"
_default_code = 5

# IO操作相关异常
class PartialReadError(LibraryError):
default_message = "Incomplete data read operation"
default_code = 6
_default_message = "Incomplete data read operation"
_default_code = 6

class FileOpenError(LibraryError):
default_message = "Failed to open file"
default_code = 28
_default_message = "Failed to open file"
_default_code = 28

class FileCloseError(LibraryError):
default_message = "Failed to close file"
default_code = 29
_default_message = "Failed to close file"
_default_code = 29

class FileWriteError(LibraryError):
default_message = "Failed to write to file"
default_code = 30
_default_message = "Failed to write to file"
_default_code = 30

class FileReadError(LibraryError):
default_message = "Failed to read from file"
default_code = 31
_default_message = "Failed to read from file"
_default_code = 31

class FileSyncError(LibraryError):
default_message = "Failed to sync file contents"
default_code = 32
_default_message = "Failed to sync file contents"
_default_code = 32

class MetadataError(LibraryError):
default_message = "Metadata inconsistency detected"
default_code = 33
_default_message = "Metadata inconsistency detected"
_default_code = 33

class BufferNotEnoughError(LibraryError):
default_message = "Insufficient buffer space"
default_code = 36
_default_message = "Insufficient buffer space"
_default_code = 36

class DeviceNotExistError(LibraryError):
default_message = "Requested device does not exist"
default_code = 44
_default_message = "Requested device does not exist"
_default_code = 44

class MeasurementNotExistError(LibraryError):
default_message = "Specified measurement does not exist"
default_code = 45
_default_message = "Specified measurement does not exist"
_default_code = 45

class InvalidQueryError(LibraryError):
default_message = "Malformed query syntax"
default_code = 46
_default_message = "Malformed query syntax"
_default_code = 46

class CompressionError(LibraryError):
default_message = "Data compression/decompression failed"
default_code = 48
_default_message = "Data compression/decompression failed"
_default_code = 48

class TableNotExistError(LibraryError):
default_message = "Requested table does not exist"
default_code = 49
_default_message = "Requested table does not exist"
_default_code = 49


class TypeNotSupportedError(LibraryError):
default_message = "Unsupported data type"
default_code = 26
_default_message = "Unsupported data type"
_default_code = 26

class TypeMismatchError(LibraryError):
default_message = "Data type mismatch"
default_code = 27
_default_message = "Data type mismatch"
_default_code = 27

ERROR_MAPPING = {
1: OOMError,
Expand All @@ -131,9 +133,14 @@ class TypeMismatchError(LibraryError):
49: TableNotExistError,
}

def get_exception(code : int, context: str = "") -> BaseException:
def get_exception(code : int, context : str = None):
if code == 0:
return None

exc_type = ERROR_MAPPING.get(code, BaseException)
return exc_type(code, message=context)
exc_type = ERROR_MAPPING.get(code)
if not exc_type:
return LibraryError(code=code, message=f"Unmapped error code: {code}")
return (exc_type, {
"code": code,
"context": context if context is not None else exc_type._default_message
})
4 changes: 2 additions & 2 deletions python/tsfile/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ class TimeseriesSchema:
def __init__(self, timeseries_name : str, data_type : TSDataType, encoding_type : TSEncoding = None, compression_type : Compressor = None):
self.timeseries_name = timeseries_name
self.data_type = data_type
self.encoding_type = encoding_type
self.compression_type = compression_type
self.encoding_type = encoding_type if encoding_type is not None else TSEncoding.PLAIN
self.compression_type = compression_type if compression_type is not None else Compressor.UNCOMPRESSED

class DeviceSchema:
device_name = None
Expand Down
2 changes: 1 addition & 1 deletion python/tsfile/tablet.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def _check_index(self, col_index : int, row_index : int):
def get_column_name_list(self):
return self.column_name_list

def get_type_list(self):
def get_data_type_list(self):
return self.type_list

def get_timestamp_list(self):
Expand Down
1 change: 1 addition & 0 deletions python/tsfile/tsfile_py_cpp.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#cython: language_level=3

from .tsfile_cpp cimport *

cdef public api inline void check_error(int errcode, const char* context=NULL) except *
cdef public api object from_c_result_set_meta_data(ResultSetMetaData schema)
cdef public api TSDataType to_c_data_type(object data_type)
Expand Down
Loading

0 comments on commit 24013da

Please sign in to comment.