AVT相机arm版本SDK

This commit is contained in:
zhangpeng
2025-04-30 09:26:04 +08:00
parent 837c870f18
commit 78a1c63a95
705 changed files with 148770 additions and 0 deletions

View File

@@ -0,0 +1,346 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
import ctypes
from vimba.c_binding import *
class VimbaCommonTest(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_decode_cstr_behavior(self):
# Expected Behavior:
# c_char_p() == ''
# c_char_p(b'foo') == 'foo'
expected = ''
actual = decode_cstr(ctypes.c_char_p())
self.assertEqual(expected, actual)
expected = 'test'
actual = decode_cstr(ctypes.c_char_p(b'test').value)
self.assertEqual(expected, actual)
def test_decode_flags_zero(self):
# Expected Behavior: In case no bytes are set the
# zero value of the Flag Enum must be returned
expected = (VmbFeatureFlags.None_,)
actual = decode_flags(VmbFeatureFlags, 0)
self.assertEqual(expected, actual)
def test_decode_flags_some(self):
# Expected Behavior: Given Integer must be decided correctly.
# the order of the fields does not matter for this test.
expected = (
VmbFeatureFlags.Write,
VmbFeatureFlags.Read,
VmbFeatureFlags.ModifyWrite
)
input_data = 0
for val in expected:
input_data |= int(val)
actual = decode_flags(VmbFeatureFlags, input_data)
# Convert both collections into a list and sort it.
# That way order doesn't matter. It is only important that values are
# decoded correctly.
self.assertEqual(list(expected).sort(), list(actual).sort())
class CBindingVimbaCTypesTest(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_enum_vmb_error(self):
self.assertEqual(VmbError.Success, 0)
self.assertEqual(VmbError.InternalFault, -1)
self.assertEqual(VmbError.ApiNotStarted, -2)
self.assertEqual(VmbError.NotFound, -3)
self.assertEqual(VmbError.BadHandle, -4)
self.assertEqual(VmbError.DeviceNotOpen, -5)
self.assertEqual(VmbError.InvalidAccess, -6)
self.assertEqual(VmbError.BadParameter, -7)
self.assertEqual(VmbError.StructSize, -8)
self.assertEqual(VmbError.MoreData, -9)
self.assertEqual(VmbError.WrongType, -10)
self.assertEqual(VmbError.InvalidValue, -11)
self.assertEqual(VmbError.Timeout, -12)
self.assertEqual(VmbError.Other, -13)
self.assertEqual(VmbError.Resources, -14)
self.assertEqual(VmbError.InvalidCall, -15)
self.assertEqual(VmbError.NoTL, -16)
self.assertEqual(VmbError.NotImplemented_, -17)
self.assertEqual(VmbError.NotSupported, -18)
self.assertEqual(VmbError.Incomplete, -19)
self.assertEqual(VmbError.IO, -20)
def test_enum_vmb_pixel_format(self):
self.assertEqual(VmbPixelFormat.Mono8, 0x01080001)
self.assertEqual(VmbPixelFormat.Mono10, 0x01100003)
self.assertEqual(VmbPixelFormat.Mono10p, 0x010A0046)
self.assertEqual(VmbPixelFormat.Mono12, 0x01100005)
self.assertEqual(VmbPixelFormat.Mono12Packed, 0x010C0006)
self.assertEqual(VmbPixelFormat.Mono12p, 0x010C0047)
self.assertEqual(VmbPixelFormat.Mono14, 0x01100025)
self.assertEqual(VmbPixelFormat.Mono16, 0x01100007)
self.assertEqual(VmbPixelFormat.BayerGR8, 0x01080008)
self.assertEqual(VmbPixelFormat.BayerRG8, 0x01080009)
self.assertEqual(VmbPixelFormat.BayerGB8, 0x0108000A)
self.assertEqual(VmbPixelFormat.BayerBG8, 0x0108000B)
self.assertEqual(VmbPixelFormat.BayerGR10, 0x0110000C)
self.assertEqual(VmbPixelFormat.BayerRG10, 0x0110000D)
self.assertEqual(VmbPixelFormat.BayerGB10, 0x0110000E)
self.assertEqual(VmbPixelFormat.BayerBG10, 0x0110000F)
self.assertEqual(VmbPixelFormat.BayerGR12, 0x01100010)
self.assertEqual(VmbPixelFormat.BayerRG12, 0x01100011)
self.assertEqual(VmbPixelFormat.BayerGB12, 0x01100012)
self.assertEqual(VmbPixelFormat.BayerBG12, 0x01100013)
self.assertEqual(VmbPixelFormat.BayerGR12Packed, 0x010C002A)
self.assertEqual(VmbPixelFormat.BayerRG12Packed, 0x010C002B)
self.assertEqual(VmbPixelFormat.BayerGB12Packed, 0x010C002C)
self.assertEqual(VmbPixelFormat.BayerBG12Packed, 0x010C002D)
self.assertEqual(VmbPixelFormat.BayerGR10p, 0x010A0056)
self.assertEqual(VmbPixelFormat.BayerRG10p, 0x010A0058)
self.assertEqual(VmbPixelFormat.BayerGB10p, 0x010A0054)
self.assertEqual(VmbPixelFormat.BayerBG10p, 0x010A0052)
self.assertEqual(VmbPixelFormat.BayerGR12p, 0x010C0057)
self.assertEqual(VmbPixelFormat.BayerRG12p, 0x010C0059)
self.assertEqual(VmbPixelFormat.BayerGB12p, 0x010C0055)
self.assertEqual(VmbPixelFormat.BayerBG12p, 0x010C0053)
self.assertEqual(VmbPixelFormat.BayerGR16, 0x0110002E)
self.assertEqual(VmbPixelFormat.BayerRG16, 0x0110002F)
self.assertEqual(VmbPixelFormat.BayerGB16, 0x01100030)
self.assertEqual(VmbPixelFormat.BayerBG16, 0x01100031)
self.assertEqual(VmbPixelFormat.Rgb8, 0x02180014)
self.assertEqual(VmbPixelFormat.Bgr8, 0x02180015)
self.assertEqual(VmbPixelFormat.Rgb10, 0x02300018)
self.assertEqual(VmbPixelFormat.Bgr10, 0x02300019)
self.assertEqual(VmbPixelFormat.Rgb12, 0x0230001A)
self.assertEqual(VmbPixelFormat.Bgr12, 0x0230001B)
self.assertEqual(VmbPixelFormat.Rgb14, 0x0230005E)
self.assertEqual(VmbPixelFormat.Bgr14, 0x0230004A)
self.assertEqual(VmbPixelFormat.Rgb16, 0x02300033)
self.assertEqual(VmbPixelFormat.Bgr16, 0x0230004B)
self.assertEqual(VmbPixelFormat.Argb8, 0x02200016)
self.assertEqual(VmbPixelFormat.Rgba8, 0x02200016)
self.assertEqual(VmbPixelFormat.Bgra8, 0x02200017)
self.assertEqual(VmbPixelFormat.Rgba10, 0x0240005F)
self.assertEqual(VmbPixelFormat.Bgra10, 0x0240004C)
self.assertEqual(VmbPixelFormat.Rgba12, 0x02400061)
self.assertEqual(VmbPixelFormat.Bgra12, 0x0240004E)
self.assertEqual(VmbPixelFormat.Rgba14, 0x02400063)
self.assertEqual(VmbPixelFormat.Bgra14, 0x02400050)
self.assertEqual(VmbPixelFormat.Rgba16, 0x02400064)
self.assertEqual(VmbPixelFormat.Bgra16, 0x02400051)
self.assertEqual(VmbPixelFormat.Yuv411, 0x020C001E)
self.assertEqual(VmbPixelFormat.Yuv422, 0x0210001F)
self.assertEqual(VmbPixelFormat.Yuv444, 0x02180020)
self.assertEqual(VmbPixelFormat.YCbCr411_8_CbYYCrYY, 0x020C003C)
self.assertEqual(VmbPixelFormat.YCbCr422_8_CbYCrY, 0x02100043)
self.assertEqual(VmbPixelFormat.YCbCr8_CbYCr, 0x0218003A)
def test_enum_vmb_interface(self):
self.assertEqual(VmbInterface.Unknown, 0)
self.assertEqual(VmbInterface.Firewire, 1)
self.assertEqual(VmbInterface.Ethernet, 2)
self.assertEqual(VmbInterface.Usb, 3)
self.assertEqual(VmbInterface.CL, 4)
self.assertEqual(VmbInterface.CSI2, 5)
def test_enum_vmb_access_mode(self):
self.assertEqual(VmbAccessMode.None_, 0)
self.assertEqual(VmbAccessMode.Full, 1)
self.assertEqual(VmbAccessMode.Read, 2)
self.assertEqual(VmbAccessMode.Config, 4)
self.assertEqual(VmbAccessMode.Lite, 8)
def test_enum_vmb_feature_data(self):
self.assertEqual(VmbFeatureData.Unknown, 0)
self.assertEqual(VmbFeatureData.Int, 1)
self.assertEqual(VmbFeatureData.Float, 2)
self.assertEqual(VmbFeatureData.Enum, 3)
self.assertEqual(VmbFeatureData.String, 4)
self.assertEqual(VmbFeatureData.Bool, 5)
self.assertEqual(VmbFeatureData.Command, 6)
self.assertEqual(VmbFeatureData.Raw, 7)
self.assertEqual(VmbFeatureData.None_, 8)
def test_enum_vmb_feature_persist(self):
self.assertEqual(VmbFeaturePersist.All, 0)
self.assertEqual(VmbFeaturePersist.Streamable, 1)
self.assertEqual(VmbFeaturePersist.NoLUT, 2)
def test_enum_vmb_feature_visibility(self):
self.assertEqual(VmbFeatureVisibility.Unknown, 0)
self.assertEqual(VmbFeatureVisibility.Beginner, 1)
self.assertEqual(VmbFeatureVisibility.Expert, 2)
self.assertEqual(VmbFeatureVisibility.Guru, 3)
self.assertEqual(VmbFeatureVisibility.Invisible, 4)
def test_enum_vmb_feature_flags(self):
self.assertEqual(VmbFeatureFlags.None_, 0)
self.assertEqual(VmbFeatureFlags.Read, 1)
self.assertEqual(VmbFeatureFlags.Write, 2)
self.assertEqual(VmbFeatureFlags.Volatile, 8)
self.assertEqual(VmbFeatureFlags.ModifyWrite, 16)
def test_enum_vmb_frame_status(self):
self.assertEqual(VmbFrameStatus.Complete, 0)
self.assertEqual(VmbFrameStatus.Incomplete, -1)
self.assertEqual(VmbFrameStatus.TooSmall, -2)
self.assertEqual(VmbFrameStatus.Invalid, -3)
def test_enum_vmd_frame_flags(self):
self.assertEqual(VmbFrameFlags.None_, 0)
self.assertEqual(VmbFrameFlags.Dimension, 1)
self.assertEqual(VmbFrameFlags.Offset, 2)
self.assertEqual(VmbFrameFlags.FrameID, 4)
self.assertEqual(VmbFrameFlags.Timestamp, 8)
class VimbaCTest(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_call_vimba_c_valid(self):
# Expectation for valid call: No exceptions, no errors
expected_ver_info = (1, 9, 0)
ver_info = VmbVersionInfo()
call_vimba_c('VmbVersionQuery', byref(ver_info), sizeof(ver_info))
ver_info = (ver_info.major, ver_info.minor, ver_info.patch)
# Not an actual check for compatibility. Just make sure a sensible value was filled
self.assertGreaterEqual(ver_info, expected_ver_info)
def test_call_vimba_c_invalid_func_name(self):
# Expectation: An invalid function name must throw an AttributeError
ver_info = VmbVersionInfo()
self.assertRaises(AttributeError, call_vimba_c, 'VmbVersionQuer', byref(ver_info),
sizeof(ver_info))
def test_call_vimba_c_invalid_arg_number(self):
# Expectation: Invalid number of arguments with sane types.
# must lead to TypeErrors
ver_info = VmbVersionInfo()
self.assertRaises(TypeError, call_vimba_c, 'VmbVersionQuery', byref(ver_info))
def test_call_vimba_c_invalid_arg_type(self):
# Expectation: Arguments with invalid types must lead to TypeErrors
# Call with unexpected base types
self.assertRaises(ctypes.ArgumentError, call_vimba_c, 'VmbVersionQuery', 0, 'hi')
# Call with valid ctypes used wrongly
ver_info = VmbVersionInfo()
self.assertRaises(ctypes.ArgumentError, call_vimba_c, 'VmbVersionQuery', byref(ver_info),
ver_info)
def test_call_vimba_c_exception(self):
# Expectation: Errors returned from the C-Layer must be mapped
# to a special Exception Type call VimbaCError. This error must
# contain the returned Error Code from the failed C-Call.
# VmbVersionQuery has two possible Errors (taken from VimbaC.h):
# - VmbErrorStructSize: The given struct size is not valid for this version of the API
# - VmbErrorBadParameter: If "pVersionInfo" is NULL.
ver_info = VmbVersionInfo()
try:
call_vimba_c('VmbVersionQuery', byref(ver_info), sizeof(ver_info) - 1)
self.fail("Previous call must raise Exception.")
except VimbaCError as e:
self.assertEqual(e.get_error_code(), VmbError.StructSize)
try:
call_vimba_c('VmbVersionQuery', None, sizeof(ver_info))
self.fail("Previous call must raise Exception.")
except VimbaCError as e:
self.assertEqual(e.get_error_code(), VmbError.BadParameter)
class ImageTransformTest(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_call_vimba_image_transform_valid(self):
# Expectation for valid call: No exceptions, no errors
expected_ver_info = EXPECTED_VIMBA_IMAGE_TRANSFORM_VERSION
v = VmbUint32()
call_vimba_image_transform('VmbGetVersion', byref(v))
ver_info = str(v.value >> 24 & 0xff) + '.' + str(v.value >> 16 & 0xff)
self.assertEqual(expected_ver_info, ver_info)
def test_call_vimba_c_invalid_func_name(self):
# Expectation: An invalid function name must throw an AttributeError
v = VmbUint32()
self.assertRaises(AttributeError, call_vimba_image_transform, 'VmbGetVersio', byref(v))
def test_call_vimba_c_invalid_arg_number(self):
# Expectation: Invalid number of arguments with sane types must lead to TypeErrors
self.assertRaises(TypeError, call_vimba_image_transform, 'VmbGetVersion')
def test_call_vimba_c_invalid_arg_type(self):
# Expectation: Arguments with invalid types must lead to TypeErrors
self.assertRaises(ctypes.ArgumentError, call_vimba_image_transform, 'VmbGetVersion',
VmbDouble())
self.assertRaises(ctypes.ArgumentError, call_vimba_image_transform, 'VmbGetVersion', 0)
self.assertRaises(ctypes.ArgumentError, call_vimba_image_transform, 'VmbGetVersion',
'invalid')
def test_call_vimba_c_exception(self):
# Expectation: Failed operations must raise a VimbaCError
self.assertRaises(VimbaCError, call_vimba_image_transform, 'VmbGetVersion', None)

View File

@@ -0,0 +1,176 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
from vimba import *
class InterfaceTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
self.vimba._startup()
inters = self.vimba.get_all_interfaces()
if not inters:
self.vimba._shutdown()
self.skipTest('No Interface available to test against. Abort.')
def tearDown(self):
self.vimba._shutdown()
def test_interface_decode_id(self):
# Expectation all interface ids can be decoded in something not ''
for i in self.vimba.get_all_interfaces():
self.assertNotEqual(i.get_id(), '')
def test_interface_decode_type(self):
# Expectation all interface types be in interface types
excpected = (
InterfaceType.Firewire,
InterfaceType.Ethernet,
InterfaceType.Usb,
InterfaceType.CL,
InterfaceType.CSI2,
)
for i in self.vimba.get_all_interfaces():
self.assertIn(i.get_type(), excpected)
def test_interface_decode_name(self):
# Expectation all interface names can be decoded in something not ''
for i in self.vimba.get_all_interfaces():
self.assertNotEqual(i.get_name(), '')
def test_interface_decode_serial(self):
# Expectation: Serials can be '' on some interfaces. This test success
# if get serial does not raise
for i in self.vimba.get_all_interfaces():
self.assertNoRaise(i.get_serial)
def test_interface_get_all_features(self):
# Expectation: Call get_all_features raises RuntimeError outside of with
# Inside of with return a non empty set
with self.vimba.get_all_interfaces()[0] as inter:
self.assertNotEqual(inter.get_all_features(), ())
def test_interface_get_features_affected_by(self):
# Expectation: Call get_features_affected_by raises RuntimeError outside of with.
# Inside with it must either return and empty set if the given feature has no affected
# Feature or a set off affected features
with self.vimba.get_all_interfaces()[0] as inter:
try:
affects_feats = inter.get_feature_by_name('DeviceUpdateList')
except VimbaFeatureError:
self.skipTest('Test requires Feature \'DeviceUpdateList\'.')
try:
not_affects_feats = inter.get_feature_by_name('DeviceCount')
except VimbaFeatureError:
self.skipTest('Test requires Feature \'DeviceCount\'.')
self.assertTrue(affects_feats.has_affected_features())
self.assertNotEquals(inter.get_features_affected_by(affects_feats), ())
self.assertFalse(not_affects_feats.has_affected_features())
self.assertEquals(inter.get_features_affected_by(not_affects_feats), ())
def test_interface_get_features_selected_by(self):
# Expectation: Call get_features_selected_by raises RuntimeError outside of with.
# Inside with it must either return and empty set if the given feature has no selected
# Feature or a set off affected features
with self.vimba.get_all_interfaces()[0] as inter:
try:
selects_feats = inter.get_feature_by_name('DeviceSelector')
except VimbaFeatureError:
self.skipTest('Test requires Feature \'DeviceSelector\'.')
try:
not_selects_feats = inter.get_feature_by_name('DeviceCount')
except VimbaFeatureError:
self.skipTest('Test requires Feature \'DeviceCount\'.')
self.assertTrue(selects_feats.has_selected_features())
self.assertNotEquals(inter.get_features_selected_by(selects_feats), ())
self.assertFalse(not_selects_feats.has_selected_features())
self.assertEquals(inter.get_features_selected_by(not_selects_feats), ())
def test_interface_get_features_by_type(self):
# Expectation: Call get_features_by_type raises RuntimeError outside of with
# Inside of with return a non empty set for IntFeature (DeviceCount is IntFeature)
with self.vimba.get_all_interfaces()[0] as inter:
self.assertNotEqual(inter.get_features_by_type(IntFeature), ())
def test_interface_get_features_by_category(self):
# Expectation: Call get_features_by_category raises RuntimeError outside of with
# Inside of with return a non empty set for /DeviceEnumeration)
with self.vimba.get_all_interfaces()[0] as inter:
self.assertNotEqual(inter.get_features_by_category('/DeviceEnumeration'), ())
def test_interface_get_feature_by_name(self):
# Expectation: Call get_feature_by_name raises RuntimeError outside of with
# Inside of with return dont raise VimbaFeatureError for 'DeviceCount'
# A invalid name must raise VimbaFeatureError
with self.vimba.get_all_interfaces()[0] as inter:
self.assertNoRaise(inter.get_feature_by_name, 'DeviceCount')
self.assertRaises(VimbaFeatureError, inter.get_feature_by_name, 'Invalid Name')
def test_interface_context_manager_reentrancy(self):
# Expectation: Implemented Context Manager must be reentrant, not causing
# multiple interface openings (would cause C-Errors)
with self.vimba.get_all_interfaces()[0] as inter:
with inter:
with inter:
pass
def test_interface_api_context_sensitivity_inside_context(self):
# Expectation: Interface has functions that shall only be callable inside the Context,
# calling outside must cause a runtime error. This test check only if the RuntimeErrors
# are triggered then called Outside of the with block.
inter = self.vimba.get_all_interfaces()[0]
self.assertRaises(RuntimeError, inter.read_memory, 0, 0)
self.assertRaises(RuntimeError, inter.write_memory, 0, b'foo')
self.assertRaises(RuntimeError, inter.read_registers, ())
self.assertRaises(RuntimeError, inter.write_registers, {0: 0})
self.assertRaises(RuntimeError, inter.get_all_features)
# Enter scope to get handle on Features as valid parameters for the test:
# Don't to this in production code because the features will be invalid if used.
with inter:
feat = inter.get_all_features()[0]
self.assertRaises(RuntimeError, inter.get_features_affected_by, feat)
self.assertRaises(RuntimeError, inter.get_features_selected_by, feat)
self.assertRaises(RuntimeError, inter.get_features_by_type, IntFeature)
self.assertRaises(RuntimeError, inter.get_features_by_category, 'foo')
self.assertRaises(RuntimeError, inter.get_feature_by_name, 'foo')

View File

@@ -0,0 +1,84 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
from vimba.util import *
class TestObj:
@LeaveContextOnCall()
def __init__(self):
pass
@EnterContextOnCall()
def __enter__(self):
pass
@LeaveContextOnCall()
def __exit__(self, _1, _2, _3):
pass
@RaiseIfOutsideContext()
def works_inside_context(self):
pass
@RaiseIfInsideContext()
def works_outside_context(self):
pass
class ContextDecoratorTest(unittest.TestCase):
def setUp(self):
self.test_obj = TestObj()
def tearDown(self):
pass
def test_raise_if_inside_context(self):
# Expectation: a decorated method must raise a RuntimeError if a
# Decorated function is called within a with - statement and
# run properly outside of the context.
self.assertNoRaise(self.test_obj.works_outside_context)
with self.test_obj:
self.assertRaises(RuntimeError, self.test_obj.works_outside_context)
self.assertNoRaise(self.test_obj.works_outside_context)
def test_raise_if_outside_context(self):
# Expectation: a decorated method must raise a RuntimeError if a
# Decorated function is called outside a with - statement and
# run properly inside of the context.
self.assertRaises(RuntimeError, self.test_obj.works_inside_context)
with self.test_obj:
self.assertNoRaise(self.test_obj.works_inside_context)
self.assertRaises(RuntimeError, self.test_obj.works_inside_context)

View File

@@ -0,0 +1,256 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
from typing import Union, Optional, Tuple, Callable, Dict, Type
from vimba.util import *
class RuntimeTypeCheckTest(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_func_mixed_args_kwargs_and_defaults(self):
# Expectation: The typecheck must be able to deal with a valid mixture of args, kwargs
# and default values.
@RuntimeTypeCheckEnable()
def test_func(a: int, b: str, c: float = 11.0 / 7.0):
pass
self.assertNoRaise(test_func, 1, '2', 0.0)
self.assertNoRaise(test_func, c=0.0, b='str', a=1)
self.assertNoRaise(test_func, 1, c=0.0, b='str')
self.assertNoRaise(test_func, 1, b='str')
self.assertNoRaise(test_func, 1, 'str')
self.assertRaises(TypeError, test_func, c=0.0, b='str', a=0.0)
self.assertRaises(TypeError, test_func, c='invalid type', b='str', a=0.0)
def test_func_no_hints(self):
# Expectation: Functions without type hints
# should not throw any type errors
@RuntimeTypeCheckEnable()
def test_func(arg1, arg2):
return str()
self.assertNoRaise(test_func, 'str', 0)
def test_func_some_hints(self):
# Expectation: Type checks are only enforced on Arguments with hint.
# Argument without hints should be accepted
@RuntimeTypeCheckEnable()
def test_func(arg1, arg2: int):
return str()
self.assertNoRaise(test_func, 'str', 0)
self.assertNoRaise(test_func, 0.5, 0)
self.assertRaises(TypeError, test_func, 'str', 0.0)
def test_object(self):
# Expectation: The runtime checker must work on Objects just as on
# functions.
class TestObject:
@RuntimeTypeCheckEnable()
def __init__(self, arg1: str, arg2: int):
pass
@RuntimeTypeCheckEnable()
def __call__(self, arg: str) -> str:
return arg
# Invalid construction
self.assertRaises(TypeError, TestObject, 0.0, 0)
obj = TestObject('str', 0)
self.assertNoRaise(obj, 'arg')
self.assertRaises(TypeError, obj, 0.0)
def test_type(self):
# Expectation: types as parameters must be detected like any other values.
@RuntimeTypeCheckEnable()
def func(arg: Type[int]):
pass
self.assertNoRaise(func, int)
self.assertRaises(TypeError, func, str)
self.assertRaises(TypeError, func, 0)
def test_union(self):
# Expectation: int and string are valid parameters. Everything else must throw
@RuntimeTypeCheckEnable()
def func(arg: Union[int, str]) -> Union[int, str]:
return arg
self.assertNoRaise(func, 0)
self.assertNoRaise(func, 'str')
self.assertRaises(TypeError, func, 0.0)
def test_optional(self):
# Expectation: For optionals the check must accept the given type or None.
# Anything else must lead to an TypeError
@RuntimeTypeCheckEnable()
def func(arg: Optional[int]) -> Optional[str]:
return str(arg)
self.assertNoRaise(func, 0)
self.assertNoRaise(func, None)
self.assertRaises(TypeError, func, 'str')
def test_tuple(self):
# Expectation: Fixed size tuples checking must verify that size and type order is
# enforced.
@RuntimeTypeCheckEnable()
def func(arg: Tuple[int, str, float]) -> Tuple[float, int, str]:
i, s, f = arg
return (f, i, s)
self.assertNoRaise(func, (1, 'str', 0.1))
self.assertRaises(TypeError, func, (1, 'str'))
self.assertRaises(TypeError, func, (1, 'str', 0.0, 'extra'))
self.assertRaises(TypeError, func, ('str1', 'str', 0.0))
def test_tuple_var_length(self):
# Expectation: Var length tuples checking must verify that contained type is enforced.
@RuntimeTypeCheckEnable()
def func(arg: Tuple[int, ...]) -> Tuple[str, ...]:
return tuple([str(i) for i in arg])
self.assertNoRaise(func, ())
self.assertNoRaise(func, (1,))
self.assertNoRaise(func, (1, 2, 3, 4, 5, 6))
self.assertRaises(TypeError, func, ('str', ))
self.assertRaises(TypeError, func, (1, 'str'))
def test_tuple_empty(self):
# Empty Tuples must satisfy the requirements to Tuple types as argument and results
@RuntimeTypeCheckEnable()
def func(arg: Tuple[int, ...]) -> Tuple[int, ...]:
return ()
self.assertNoRaise(func, ())
self.assertEqual(func(()), ())
def test_tuple_union(self):
# Tuples of union types must be detected correctly
@RuntimeTypeCheckEnable()
def func(arg: Tuple[Union[int, str], ...]):
return arg
self.assertNoRaise(func, (0,))
self.assertNoRaise(func, ('1',))
self.assertNoRaise(func, (2, 3))
self.assertNoRaise(func, ('4', '5'))
self.assertNoRaise(func, (6, '7'))
self.assertNoRaise(func, ('8', 9))
self.assertRaises(TypeError, func, (2, 0.0))
def test_dict(self):
# Expectation: Dictionaries must be detected correctly.
@RuntimeTypeCheckEnable()
def func(arg: Dict[int, str]):
pass
self.assertNoRaise(func, {0: 'ok'})
self.assertRaises(TypeError, func, None)
self.assertRaises(TypeError, func, 0)
self.assertRaises(TypeError, func, 'No Dict')
self.assertRaises(TypeError, func, {0.0: 'Err'})
self.assertRaises(TypeError, func, {0: b'bytes'})
def test_callable_no_func(self):
# Expectation: The Callable verification shall fail if given Parameter is no callable.
@RuntimeTypeCheckEnable()
def func(fn: Callable[[], None]):
fn()
self.assertRaises(TypeError, func, 'no_callable')
def test_callable_func(self):
# Expectation: A Callable without any hints must comply as long as the number of parameters
# matches to given hints. The Return Type doesn't matter if not given.
@RuntimeTypeCheckEnable()
def func(fn: Callable[[str, float], int], arg1: str, arg2: float) -> int:
return fn(arg1, arg2)
def ok(arg1, arg2):
return 0.0
def err1(arg1):
return 'str'
def err2(arg1, arg2, arg3):
return 23
self.assertNoRaise(func, ok, 'str', 0.0)
self.assertRaises(TypeError, func, err1, 'str', 0.0)
self.assertRaises(TypeError, func, err2, 'str', 0.0)
def test_callable_obj(self):
# Expectation: A Object that is callable must pass the runtime check
@RuntimeTypeCheckEnable()
def func(fn: Callable[[str], None], arg: str) -> str:
return fn(arg)
class Ok:
def __call__(self, arg: str) -> str:
return str
class Err1:
def __call__(self) -> str:
return 'Err1'
class Err2:
def __call__(self, arg1: str, arg2: str) -> str:
return arg1 + arg2
self.assertNoRaise(func, Ok(), 'str')
self.assertRaises(TypeError, func, Err1(), 'str')
self.assertRaises(TypeError, func, Err2(), 'str')
def test_callable_lambda(self):
# Expectation: RuntimeTypeCheck must behave with lambas as with functions
@RuntimeTypeCheckEnable()
def func(fn: Callable[[str, float], int], arg1: str, arg2: float) -> int:
return fn(arg1, arg2)
self.assertNoRaise(func, lambda a1, a2: 0.0, 'str', 0.0)
self.assertRaises(TypeError, func, lambda a1: 'foo', 'str', 0.0)
self.assertRaises(TypeError, func, lambda a1, a2, a3: 23, 'str', 0.0)

View File

@@ -0,0 +1,157 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
from vimba.util import *
class TracerTest(unittest.TestCase):
def setUp(self):
# Enable logging and setup hidden buffer
self.log = Log.get_instance()
self.log._test_buffer = []
self.log.enable(LOG_CONFIG_CRITICAL_CONSOLE_ONLY)
def tearDown(self):
# Disable logging and clear hidden buffer
self.log.disable()
self.log._test_buffer = None
def test_trace_inactive(self):
# Expectation: A disabled log must not contain any trace entries.
@TraceEnable()
def test_func(arg):
return str(arg)
self.log.disable()
self.assertEqual(test_func(1), '1')
self.assertFalse(self.log._test_buffer)
self.assertEqual(test_func('test'), 'test')
self.assertFalse(self.log._test_buffer)
self.assertEqual(test_func(2.0), '2.0')
self.assertFalse(self.log._test_buffer)
def test_trace_normal_exit(self):
# Expectation: Must not throw on call normal func.
# Each call traced call must add two Log entries:
@TraceEnable()
def test_func(arg):
return str(arg)
self.assertEqual(test_func(1), '1')
self.assertEqual(len(self.log._test_buffer), 2)
self.assertEqual(test_func('test'), 'test')
self.assertEqual(len(self.log._test_buffer), 4)
self.assertEqual(test_func(2.0), '2.0')
self.assertEqual(len(self.log._test_buffer), 6)
def test_trace_raised_exit(self):
# Expectation: Throws internally thrown exception and adds two log entries
# Each call traced call must add two Log entries:
@TraceEnable()
def test_func(arg):
raise TypeError('my error')
self.assertRaises(TypeError, test_func, 1)
self.assertEqual(len(self.log._test_buffer), 2)
self.assertRaises(TypeError, test_func, 'test')
self.assertEqual(len(self.log._test_buffer), 4)
self.assertRaises(TypeError, test_func, 2.0)
self.assertEqual(len(self.log._test_buffer), 6)
def test_trace_function(self):
# Expectation: Normal functions must be traceable
@TraceEnable()
def test_func():
pass
test_func()
self.assertEqual(len(self.log._test_buffer), 2)
test_func()
self.assertEqual(len(self.log._test_buffer), 4)
test_func()
self.assertEqual(len(self.log._test_buffer), 6)
def test_trace_lambda(self):
# Expectation: Lambdas must be traceable
test_lambda = TraceEnable()(lambda: 0)
test_lambda()
self.assertEqual(len(self.log._test_buffer), 2)
test_lambda()
self.assertEqual(len(self.log._test_buffer), 4)
test_lambda()
self.assertEqual(len(self.log._test_buffer), 6)
def test_trace_object(self):
# Expectation: Objects must be traceable including constructors.
class TestObj:
@TraceEnable()
def __init__(self, arg):
self.arg = arg
@TraceEnable()
def __str__(self):
return 'TestObj({})'.format(str(self.arg))
@TraceEnable()
def __repr__(self):
return 'TestObj({})'.format(repr(self.arg))
@TraceEnable()
def __call__(self):
pass
test_obj = TestObj('test')
self.assertEqual(len(self.log._test_buffer), 2)
str(test_obj)
self.assertEqual(len(self.log._test_buffer), 4)
repr(test_obj)
self.assertEqual(len(self.log._test_buffer), 6)
test_obj()
self.assertEqual(len(self.log._test_buffer), 8)

View File

@@ -0,0 +1,89 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
from vimba.c_binding import _select_vimba_home
from vimba.error import VimbaSystemError
class RankVimbaHomeCandidatesTest(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_empty_gentl_path(self):
candidates = []
with self.assertRaises(VimbaSystemError):
_select_vimba_home(candidates)
def test_empty_string(self):
candidates = ['']
with self.assertRaises(VimbaSystemError):
_select_vimba_home(candidates)
def test_single_bad_vimba_home_candidate(self):
candidates = ['/some/path']
with self.assertRaises(VimbaSystemError):
_select_vimba_home(candidates)
def test_single_good_vimba_home_candidate(self):
candidates = ['/opt/Vimba_3_1']
expected = '/opt/Vimba_3_1'
self.assertEquals(expected, _select_vimba_home(candidates))
def test_presorted_vimba_home_candidates(self):
candidates = ['/home/username/Vimba_4_0', '/opt/some/other/gentl/provider']
expected = '/home/username/Vimba_4_0'
self.assertEqual(expected, _select_vimba_home(candidates))
def test_unsorted_vimba_home_candidates(self):
candidates = ['/opt/some/other/gentl/provider', '/home/username/Vimba_4_0']
expected = '/home/username/Vimba_4_0'
self.assertEqual(expected, _select_vimba_home(candidates))
def test_many_vimba_home_candidates(self):
candidates = ['/some/random/path',
'/opt/some/gentl/provider',
'/opt/Vimba_4_0', # This should be selected
'/opt/another/gentl/provider',
'/another/incorrect/path']
expected = '/opt/Vimba_4_0'
self.assertEqual(expected, _select_vimba_home(candidates))
def test_multiple_vimba_home_directories(self):
# If multiple VIMBA_HOME directories are found an error should be raised
candidates = ['/some/random/path',
'/opt/some/gentl/provider',
'/opt/Vimba_4_0', # first installation
'/home/username/Vimba_4_0', # second installation
'/opt/another/gentl/provider',
'/another/incorrect/path']
with self.assertRaises(VimbaSystemError):
_select_vimba_home(candidates)

View File

@@ -0,0 +1,126 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
from vimba import *
class VimbaTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
def tearDown(self):
pass
def test_singleton(self):
# Expected behavior: Multiple calls to Vimba.get_instance() return the same object.
self.assertEqual(self.vimba, Vimba.get_instance())
def test_get_version(self):
# Expectation: Returned Version is not empty and does not raise any exceptions.
self.assertNotEqual(self.vimba.get_version(), "")
def test_get_camera_by_id_failure(self):
# Expected behavior: Lookup of a currently unavailable camera must throw an
# VimbaCameraError
with self.vimba:
self.assertRaises(VimbaCameraError, self.vimba.get_camera_by_id, 'Invalid ID')
def test_get_interface_by_id_failure(self):
# Expected behavior: Lookup of a currently unavailable interface must throw an
# VimbaInterfaceError
with self.vimba:
self.assertRaises(VimbaInterfaceError, self.vimba.get_interface_by_id, 'Invalid ID')
def test_get_feature_by_name_failure(self):
# Expected behavior: Lookup of a currently unavailable feature must throw an
# VimbaFeatureError
with self.vimba:
self.assertRaises(VimbaFeatureError, self.vimba.get_feature_by_name, 'Invalid ID')
def test_runtime_check_failure(self):
self.assertRaises(TypeError, self.vimba.set_network_discovery, 0.0)
with self.vimba:
# All functions with RuntimeTypeCheckEnable must return a TypeError on Failure
self.assertRaises(TypeError, self.vimba.get_camera_by_id, 0)
self.assertRaises(TypeError, self.vimba.get_interface_by_id, 1)
self.assertRaises(TypeError, self.vimba.get_feature_by_name, 0)
self.assertRaises(TypeError, self.vimba.enable_log, '-1')
self.assertRaises(TypeError, self.vimba.get_features_affected_by, '-1')
self.assertRaises(TypeError, self.vimba.get_features_selected_by, '-1')
self.assertRaises(TypeError, self.vimba.get_features_by_type, [])
self.assertRaises(TypeError, self.vimba.register_camera_change_handler, 0)
self.assertRaises(TypeError, self.vimba.unregister_camera_change_handler, 0)
self.assertRaises(TypeError, self.vimba.register_interface_change_handler, 0)
self.assertRaises(TypeError, self.vimba.unregister_interface_change_handler, 0)
def test_vimba_context_manager_reentrancy(self):
# Expectation: Implemented Context Manager must be reentrant, not causing
# multiple starts of the Vimba API (would cause C-Errors)
with self.vimba:
with self.vimba:
with self.vimba:
pass
def test_vimba_api_context_sensitity_outside_context(self):
# Expectation: Vimba has functions that shall only be callable outside the Context and
# calling within the context must cause a runtime error.
self.assertNoRaise(self.vimba.set_network_discovery, True)
with self.vimba:
self.assertRaises(RuntimeError, self.vimba.set_network_discovery, True)
self.assertNoRaise(self.vimba.set_network_discovery, True)
def test_vimba_api_context_sensitity_inside_context(self):
# Expectation: Vimba has functions that shall only be callable inside the Context and
# calling outside must cause a runtime error. This test check only if the RuntimeErrors
# are triggered then called Outside of the with block.
self.assertRaises(RuntimeError, self.vimba.read_memory, 0, 0)
self.assertRaises(RuntimeError, self.vimba.write_memory, 0, b'foo')
self.assertRaises(RuntimeError, self.vimba.read_registers, ())
self.assertRaises(RuntimeError, self.vimba.write_registers, {0: 0})
self.assertRaises(RuntimeError, self.vimba.get_all_interfaces)
self.assertRaises(RuntimeError, self.vimba.get_interface_by_id, 'id')
self.assertRaises(RuntimeError, self.vimba.get_all_cameras)
self.assertRaises(RuntimeError, self.vimba.get_camera_by_id, 'id')
self.assertRaises(RuntimeError, self.vimba.get_all_features)
# Enter scope to get handle on Features as valid parameters for the test:
# Don't to this in production code because the feature will be invalid if use.
with self.vimba:
feat = self.vimba.get_all_features()[0]
self.assertRaises(RuntimeError, self.vimba.get_features_affected_by, feat)
self.assertRaises(RuntimeError, self.vimba.get_features_selected_by, feat)
self.assertRaises(RuntimeError, self.vimba.get_features_by_type, IntFeature)
self.assertRaises(RuntimeError, self.vimba.get_features_by_category, 'foo')
self.assertRaises(RuntimeError, self.vimba.get_feature_by_name, 'foo')

View File

@@ -0,0 +1,155 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
from vimba import *
class CamAncillaryDataTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
self.vimba._startup()
try:
self.cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to lookup Camera.') from e
try:
self.cam._open()
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to open Camera.') from e
try:
self.chunk_mode = self.cam.get_feature_by_name('ChunkModeActive')
except VimbaFeatureError:
self.cam._close()
self.vimba._shutdown()
self.skipTest('Required Feature \'ChunkModeActive\' not available.')
def tearDown(self):
self.cam._close()
self.vimba._shutdown()
def test_ancillary_data_access(self):
# Expectation: Ancillary Data is None if ChunkMode is disable.
# If ChunkMode is enabled Ancillary Data shall not be None.
old_state = self.chunk_mode.get()
try:
# Disable ChunkMode, acquire frame: Ancillary Data must be None
self.chunk_mode.set(False)
self.assertIsNone(self.cam.get_frame().get_ancillary_data())
# Enable ChunkMode, acquire frame: Ancillary Data must not be None
self.chunk_mode.set(True)
self.assertIsNotNone(self.cam.get_frame().get_ancillary_data())
finally:
self.chunk_mode.set(old_state)
def test_ancillary_data_context_manager_reentrancy(self):
# Expectation: Ancillary Data Context Manager must be reentrant.
old_state = self.chunk_mode.get()
try:
self.chunk_mode.set(True)
frame = self.cam.get_frame()
anc_data = frame.get_ancillary_data()
with anc_data:
with anc_data:
with anc_data:
pass
finally:
self.chunk_mode.set(old_state)
def test_ancillary_data_api_context_sensitity(self):
# Expectation: Ancillary Data implements a Context Manager, outside of with-scope
# a runtime error should be raised on all feature related methods accessed outside of the
# context.
old_state = self.chunk_mode.get()
try:
self.chunk_mode.set(True)
frame = self.cam.get_frame()
anc_data = frame.get_ancillary_data()
# Check Access Outside Context
self.assertRaises(RuntimeError, anc_data.get_all_features)
self.assertRaises(RuntimeError, anc_data.get_features_by_type, IntFeature)
self.assertRaises(RuntimeError, anc_data.get_features_by_category, '/ChunkData')
self.assertRaises(RuntimeError, anc_data.get_feature_by_name, 'ChunkExposureTime')
with anc_data:
# Check Access after Context entry
self.assertNoRaise(anc_data.get_all_features)
self.assertNoRaise(anc_data.get_features_by_type, IntFeature)
self.assertNoRaise(anc_data.get_features_by_category, '/ChunkData')
self.assertNoRaise(anc_data.get_feature_by_name, 'ChunkExposureTime')
# Check Access after Context leaving
self.assertRaises(RuntimeError, anc_data.get_all_features)
self.assertRaises(RuntimeError, anc_data.get_features_by_type, IntFeature)
self.assertRaises(RuntimeError, anc_data.get_features_by_category, '/ChunkData')
self.assertRaises(RuntimeError, anc_data.get_feature_by_name, 'ChunkExposureTime')
finally:
self.chunk_mode.set(old_state)
def test_ancillary_data_removed_attrs(self):
# Expectation: Ancillary Data are lightweight features. Calling most Feature-Methods that
# call VimbaC Features would cause an internal error. Those error prone methods
# shall raise a RuntimeError on call.
old_state = self.chunk_mode.get()
try:
self.chunk_mode.set(True)
frame = self.cam.get_frame()
anc_data = frame.get_ancillary_data()
with anc_data:
for feat in anc_data.get_all_features():
self.assertRaises(RuntimeError, feat.get_access_mode)
self.assertRaises(RuntimeError, feat.is_readable)
self.assertRaises(RuntimeError, feat.is_writeable)
self.assertRaises(RuntimeError, feat.register_change_handler)
self.assertRaises(RuntimeError, feat.get_range)
self.assertRaises(RuntimeError, feat.get_increment)
self.assertRaises(RuntimeError, feat.set)
finally:
self.chunk_mode.set(old_state)

View File

@@ -0,0 +1,422 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
import threading
import os
from vimba import *
from vimba.frame import *
def dummy_frame_handler(cam: Camera, frame: Frame):
pass
class CamCameraTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
self.vimba._startup()
try:
self.cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to lookup Camera.') from e
self.cam.set_access_mode(AccessMode.Full)
def tearDown(self):
self.cam.set_access_mode(AccessMode.Full)
self.vimba._shutdown()
def test_camera_context_manager_access_mode(self):
# Expectation: Entering Context must not throw in cases where the current access mode is
# within get_permitted_access_modes()
permitted_modes = self.cam.get_permitted_access_modes()
for mode in permitted_modes:
self.cam.set_access_mode(mode)
try:
with self.cam:
pass
except BaseException:
self.fail()
def test_camera_context_manager_feature_discovery(self):
# Expectation: Outside of context, all features must be cleared,
# inside of context all features must be detected.
with self.cam:
self.assertNotEqual(self.cam.get_all_features(), ())
def test_camera_access_mode(self):
# Expectation: set/get access mode
self.cam.set_access_mode(AccessMode.None_)
self.assertEqual(self.cam.get_access_mode(), AccessMode.None_)
self.cam.set_access_mode(AccessMode.Full)
self.assertEqual(self.cam.get_access_mode(), AccessMode.Full)
self.cam.set_access_mode(AccessMode.Read)
self.assertEqual(self.cam.get_access_mode(), AccessMode.Read)
def test_camera_get_id(self):
# Expectation: get decoded camera id
self.assertTrue(self.cam.get_id())
def test_camera_get_name(self):
# Expectation: get decoded camera name
self.assertTrue(self.cam.get_name())
def test_camera_get_model(self):
# Expectation: get decoded camera model
self.assertTrue(self.cam.get_model())
def test_camera_get_serial(self):
# Expectation: get decoded camera serial
self.assertTrue(self.cam.get_serial())
def test_camera_get_permitted_access_modes(self):
# Expectation: get currently permitted access modes
expected = (AccessMode.None_, AccessMode.Full, AccessMode.Read, AccessMode.Config)
for mode in self.cam.get_permitted_access_modes():
self.assertIn(mode, expected)
def test_camera_get_interface_id(self):
# Expectation: get interface Id this camera is connected to
self.assertTrue(self.cam.get_interface_id())
def test_camera_get_features_affected(self):
# Expectation: Features that affect other features shall return a set of affected feature
# Features that don't affect other features shall return (). If a Feature is supplied that
# is not associated with that camera, a TypeError must be raised.
with self.cam:
try:
affect = self.cam.get_feature_by_name('Height')
except VimbaFeatureError as e:
raise unittest.SkipTest('Failed to lookup Feature Height') from e
try:
not_affect = self.cam.get_feature_by_name('AcquisitionFrameCount')
except VimbaFeatureError as e:
raise unittest.SkipTest('Failed to lookup Feature AcquisitionFrameCount') from e
self.assertEqual(self.cam.get_features_affected_by(not_affect), ())
try:
payload_size = self.cam.get_feature_by_name('PayloadSize')
except VimbaFeatureError as e:
raise unittest.SkipTest('Failed to lookup Feature PayloadSize') from e
self.assertIn(payload_size, self.cam.get_features_affected_by(affect))
def test_camera_frame_generator_limit_set(self):
# Expectation: The Frame generator fetches the given number of images.
with self.cam:
self.assertEqual(len([i for i in self.cam.get_frame_generator(0)]), 0)
self.assertEqual(len([i for i in self.cam.get_frame_generator(1)]), 1)
self.assertEqual(len([i for i in self.cam.get_frame_generator(7)]), 7)
self.assertEqual(len([i for i in self.cam.get_frame_generator(11)]), 11)
def test_camera_frame_generator_error(self):
# Expectation: The Frame generator raises a ValueError on a
# negative limit and the camera raises an ValueError
# if the camera is not opened.
# generator execution must throw if streaming is enabled
with self.cam:
# Check limits
self.assertRaises(ValueError, self.cam.get_frame_generator, -1)
self.assertRaises(ValueError, self.cam.get_frame_generator, 1, 0)
self.assertRaises(ValueError, self.cam.get_frame_generator, 1, -1)
self.cam.start_streaming(dummy_frame_handler, 5)
self.assertRaises(VimbaCameraError, self.cam.get_frame)
self.assertRaises(VimbaCameraError, next, self.cam.get_frame_generator(1))
# Stop Streaming: Everything should be fine.
self.cam.stop_streaming()
self.assertNoRaise(self.cam.get_frame)
self.assertNoRaise(next, self.cam.get_frame_generator(1))
def test_camera_get_frame(self):
# Expectation: Gets single Frame without any exception. Image data must be set.
# If a zero or negative timeouts must lead to a ValueError.
with self.cam:
self.assertRaises(ValueError, self.cam.get_frame, 0)
self.assertRaises(ValueError, self.cam.get_frame, -1)
self.assertNoRaise(self.cam.get_frame)
self.assertEqual(type(self.cam.get_frame()), Frame)
def test_camera_capture_error_outside_vimba_scope(self):
# Expectation: Camera access outside of Vimba scope must lead to a RuntimeError
gener = None
with self.cam:
gener = self.cam.get_frame_generator(1)
# Shutdown API
self.vimba._shutdown()
# Access invalid Iterator
self.assertRaises(RuntimeError, next, gener)
def test_camera_capture_error_outside_camera_scope(self):
# Expectation: Camera access outside of Camera scope must lead to a RuntimeError
gener = None
with self.cam:
gener = self.cam.get_frame_generator(1)
self.assertRaises(RuntimeError, next, gener)
def test_camera_capture_timeout(self):
# Expectation: Camera access outside of Camera scope must lead to a VimbaTimeout
with self.cam:
self.assertRaises(VimbaTimeout, self.cam.get_frame, 1)
def test_camera_is_streaming(self):
# Expectation: After start_streaming() is_streaming() must return true. After stop it must
# return false. If the camera context is left without stop_streaming(), leaving
# the context must stop streaming.
# Normal Operation
self.assertEqual(self.cam.is_streaming(), False)
with self.cam:
self.cam.start_streaming(dummy_frame_handler)
self.assertEqual(self.cam.is_streaming(), True)
self.cam.stop_streaming()
self.assertEqual(self.cam.is_streaming(), False)
# Missing the stream stop. Close must stop streaming
with self.cam:
self.cam.start_streaming(dummy_frame_handler, 5)
self.assertEqual(self.cam.is_streaming(), True)
self.assertEqual(self.cam.is_streaming(), False)
def test_camera_streaming_error_frame_count(self):
# Expectation: A negative or zero frame_count must lead to an value error
with self.cam:
self.assertRaises(ValueError, self.cam.start_streaming, dummy_frame_handler, 0)
self.assertRaises(ValueError, self.cam.start_streaming, dummy_frame_handler, -1)
def test_camera_streaming(self):
# Expectation: A given frame_handler must be executed for each buffered frame.
class FrameHandler:
def __init__(self, frame_count):
self.cnt = 0
self.frame_count = frame_count
self.event = threading.Event()
def __call__(self, cam: Camera, frame: Frame):
self.cnt += 1
if self.cnt == self.frame_count:
self.event.set()
timeout = 5.0
frame_count = 10
handler = FrameHandler(frame_count)
with self.cam:
try:
self.cam.start_streaming(handler, frame_count)
# Wait until the FrameHandler has been executed for each queued frame
self.assertTrue(handler.event.wait(timeout))
finally:
self.cam.stop_streaming()
def test_camera_streaming_queue(self):
# Expectation: A given frame must be reused if it is enqueued again.
class FrameHandler:
def __init__(self, frame_count):
self.cnt = 0
self.frame_count = frame_count
self.event = threading.Event()
def __call__(self, cam: Camera, frame: Frame):
self.cnt += 1
if self.cnt == self.frame_count:
self.event.set()
cam.queue_frame(frame)
timeout = 5.0
frame_count = 5
frame_reuse = 2
handler = FrameHandler(frame_count * frame_reuse)
with self.cam:
try:
self.cam.start_streaming(handler, frame_count)
# Wait until the FrameHandler has been executed for each queued frame
self.assertTrue(handler.event.wait(timeout))
finally:
self.cam.stop_streaming()
def test_camera_runtime_type_check(self):
def valid_handler(cam, frame):
pass
def invalid_handler_1(cam):
pass
def invalid_handler_2(cam, frame, extra):
pass
self.assertRaises(TypeError, self.cam.set_access_mode, -1)
with self.cam:
# Expectation: raise TypeError on passing invalid parameters
self.assertRaises(TypeError, self.cam.get_frame, 'hi')
self.assertRaises(TypeError, self.cam.get_features_affected_by, 'No Feature')
self.assertRaises(TypeError, self.cam.get_features_selected_by, 'No Feature')
self.assertRaises(TypeError, self.cam.get_features_by_type, 0.0)
self.assertRaises(TypeError, self.cam.get_feature_by_name, 0)
self.assertRaises(TypeError, self.cam.get_frame_generator, '3')
self.assertRaises(TypeError, self.cam.get_frame_generator, 0, 'foo')
self.assertRaises(TypeError, self.cam.start_streaming, valid_handler, 'no int')
self.assertRaises(TypeError, self.cam.start_streaming, invalid_handler_1)
self.assertRaises(TypeError, self.cam.start_streaming, invalid_handler_2)
self.assertRaises(TypeError, self.cam.save_settings, 0, PersistType.All)
self.assertRaises(TypeError, self.cam.save_settings, 'foo.xml', 'false type')
def test_camera_save_load_settings(self):
# Expectation: After settings export a settings change must be reverted by loading a
# Previously saved configuration.
file_name = 'test_save_load_settings.xml'
with self.cam:
feat_height = self.cam.get_feature_by_name('Height')
old_val = feat_height.get()
self.cam.save_settings(file_name, PersistType.All)
min_, max_ = feat_height.get_range()
inc = feat_height.get_increment()
feat_height.set(max_ - min_ - inc)
self.cam.load_settings(file_name, PersistType.All)
os.remove(file_name)
self.assertEqual(old_val, feat_height.get())
def test_camera_save_settings_verify_path(self):
# Expectation: Valid files end with .xml and can be either a absolute path or relative
# path to the given File. Everything else is a ValueError.
valid_paths = (
'valid1.xml',
os.path.join('.', 'valid2.xml'),
os.path.join('Tests', 'valid3.xml'),
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'valid4.xml'),
)
with self.cam:
self.assertRaises(ValueError, self.cam.save_settings, 'inval.xm', PersistType.All)
for path in valid_paths:
self.assertNoRaise(self.cam.save_settings, path, PersistType.All)
os.remove(path)
def test_camera_load_settings_verify_path(self):
# Expectation: Valid files end with .xml and must exist before before any execution.
valid_paths = (
'valid1.xml',
os.path.join('.', 'valid2.xml'),
os.path.join('Tests', 'valid3.xml'),
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'valid4.xml'),
)
with self.cam:
self.assertRaises(ValueError, self.cam.load_settings, 'inval.xm', PersistType.All)
for path in valid_paths:
self.assertRaises(ValueError, self.cam.load_settings, path, PersistType.All)
for path in valid_paths:
self.cam.save_settings(path, PersistType.All)
self.assertNoRaise(self.cam.load_settings, path, PersistType.All)
os.remove(path)
def test_camera_context_manager_reentrancy(self):
# Expectation: Camera Context Manager must be reentrant. Multiple calls to _open
# must be prevented (would cause VimbaC - Error)
with self.cam:
with self.cam:
with self.cam:
pass
def test_camera_api_context_sensitity_outside_context(self):
# Expectation: Call set_access_mode withing with scope must raise a RuntimeError
with self.cam:
self.assertRaises(RuntimeError, self.cam.set_access_mode)
def test_camera_api_context_sensitity_inside_context(self):
# Expectation: Most Camera related functions are only valid then called within the given
# Context. If called from Outside a runtime error must be raised.
self.assertRaises(RuntimeError, self.cam.read_memory)
self.assertRaises(RuntimeError, self.cam.write_memory)
self.assertRaises(RuntimeError, self.cam.read_registers)
self.assertRaises(RuntimeError, self.cam.write_registers)
self.assertRaises(RuntimeError, self.cam.get_all_features)
self.assertRaises(RuntimeError, self.cam.get_features_affected_by)
self.assertRaises(RuntimeError, self.cam.get_features_selected_by)
self.assertRaises(RuntimeError, self.cam.get_features_by_type)
self.assertRaises(RuntimeError, self.cam.get_features_by_category)
self.assertRaises(RuntimeError, self.cam.get_feature_by_name)
self.assertRaises(RuntimeError, self.cam.get_frame_generator)
self.assertRaises(RuntimeError, self.cam.get_frame)
self.assertRaises(RuntimeError, self.cam.start_streaming)
self.assertRaises(RuntimeError, self.cam.stop_streaming)
self.assertRaises(RuntimeError, self.cam.queue_frame)
self.assertRaises(RuntimeError, self.cam.get_pixel_formats)
self.assertRaises(RuntimeError, self.cam.get_pixel_format)
self.assertRaises(RuntimeError, self.cam.set_pixel_format)
self.assertRaises(RuntimeError, self.cam.save_settings)
self.assertRaises(RuntimeError, self.cam.load_settings)

View File

@@ -0,0 +1,831 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
import threading
from vimba import *
from vimba.feature import *
class CamBaseFeatureTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
self.vimba._startup()
try:
self.cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to lookup Camera.') from e
try:
self.cam._open()
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to open Camera.') from e
try:
self.height = self.cam.get_feature_by_name('Height')
except VimbaCameraError:
self.cam._close()
self.vimba._shutdown()
self.skipTest('Required Feature \'Height\' not available.')
def tearDown(self):
self.cam._close()
self.vimba._shutdown()
def test_get_name(self):
# Expectation: Return decoded FeatureName
self.assertEqual(self.height.get_name(), 'Height')
def test_get_flags(self):
# Expectation: Return decoded FeatureFlags
self.assertEqual(self.height.get_flags(), (FeatureFlags.Read, FeatureFlags.Write))
def test_get_category(self):
# Expectation: Return decoded category
self.assertNotEqual(self.height.get_category(), '')
def test_get_display_name(self):
# Expectation: Return decoded category
self.assertEqual(self.height.get_display_name(), 'Height')
def test_get_polling_time(self):
# Expectation: Return polling time. Only volatile features return
# anything other than zero.
self.assertEqual(self.height.get_polling_time(), 0)
def test_get_unit(self):
# Expectation: If Unit exists, return unit else return ''
self.assertEqual(self.height.get_unit(), '')
def test_get_representation(self):
# Expectation: Get numeric representation if existing else ''
self.assertEqual(self.height.get_representation(), '')
def test_get_visibility(self):
# Expectation: Get UI Visibility
self.assertEqual(self.height.get_visibility(), FeatureVisibility.Beginner)
def test_get_tooltip(self):
# Expectation: Shall not raise anything
self.assertNoRaise(self.height.get_tooltip)
def test_get_description(self):
# Expectation: Get decoded description
self.assertNotEqual(self.height.get_description(), '')
def test_get_sfnc_namespace(self):
# Expectation: Get decoded sfnc namespace
self.assertNotEqual(self.height.get_sfnc_namespace(), '')
def test_is_streamable(self):
# Expectation: Streamable features shall return True, others False
self.assertNoRaise(self.height.is_streamable)
def test_has_affected_features(self):
# Expectation:Features that affect features shall return True, others False
self.assertTrue(self.height.has_affected_features())
def test_has_selected_features(self):
# Expectation:Features that select features shall return True, others False
self.assertFalse(self.height.has_selected_features())
def test_get_access_mode(self):
# Expectation: Read/Write Features return (True, True), ReadOnly return (True, False)
self.assertEqual(self.height.get_access_mode(), (True, True))
def test_is_readable(self):
# Expectation: True if feature grant read access else False
self.assertTrue(self.height.is_readable())
def test_is_writeable(self):
# Expectation: True if feature grant write access else False
self.assertTrue(self.height.is_writeable())
def test_change_handler(self):
# Expectation: A given change handler is executed on value change.
# Adding the same handler multiple times shall not lead to multiple executions.
# The same goes for double unregister.
class Handler:
def __init__(self):
self.event = threading.Event()
self.call_cnt = 0
def __call__(self, feat):
self.call_cnt += 1
self.event.set()
handler = Handler()
self.height.register_change_handler(handler)
self.height.register_change_handler(handler)
tmp = self.height.get()
min_, _ = self.height.get_range()
inc = self.height.get_increment()
if min_ <= tmp - inc:
self.height.set(tmp - inc)
else:
self.height.set(tmp + inc)
handler.event.wait()
self.height.unregister_change_handler(handler)
self.height.unregister_change_handler(handler)
self.height.set(tmp)
self.assertEqual(handler.call_cnt, 1)
def test_stringify_features(self):
# Expectation: Each Feature must have a __str__ method. Depending on the Feature
# current Values are queried, this can fail. In those cases, all exceptions are
# fetched -> all features must be strinify able without raising any exception
for feat in self.vimba.get_all_features():
self.assertNoRaise(str, feat)
for feat in self.cam.get_all_features():
self.assertNoRaise(str, feat)
class CamBoolFeatureTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
self.vimba._startup()
try:
self.feat = self.vimba.get_feature_by_name('UsbTLIsPresent')
except VimbaFeatureError:
self.vimba._shutdown()
self.skipTest('Required Feature \'UsbTLIsPresent\' not available.')
def tearDown(self):
self.vimba._shutdown()
def test_get_type(self):
# Expectation: BoolFeature must return BoolFeature on get_type
self.assertEqual(self.feat.get_type(), BoolFeature)
def test_get(self):
# Expectation: returns current boolean value.
self.assertNoRaise(self.feat.get)
def test_set(self):
# Expectation: Raises invalid Access on non-writeable features.
self.assertRaises(VimbaFeatureError, self.feat.set, True)
class CamCommandFeatureTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
self.vimba._startup()
try:
self.feat = self.vimba.get_feature_by_name('ActionCommand')
except VimbaFeatureError:
self.vimba._shutdown()
self.skipTest('Required Feature \'ActionCommand\' not available.')
def tearDown(self):
self.vimba._shutdown()
def test_get_type(self):
# Expectation: CommandFeature must return CommandFeature on get_type
self.assertEqual(self.feat.get_type(), CommandFeature)
class CamEnumFeatureTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
self.vimba._startup()
try:
self.cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to lookup Camera.') from e
try:
self.cam._open()
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to open Camera.') from e
try:
self.feat_r = self.cam.get_feature_by_name('DeviceScanType')
except VimbaFeatureError:
self.cam._close()
self.vimba._shutdown()
self.skipTest('Required Feature \'DeviceScanType\' not available.')
try:
self.feat_rw = self.cam.get_feature_by_name('AcquisitionMode')
except VimbaFeatureError:
self.cam._close()
self.vimba._shutdown()
self.skipTest('Required Feature \'AcquisitionMode\' not available.')
def tearDown(self):
self.cam._close()
self.vimba._shutdown()
def test_get_type(self):
# Expectation: EnumFeature must return EnumFeature on get_type
self.assertEqual(self.feat_r.get_type(), EnumFeature)
self.assertEqual(self.feat_rw.get_type(), EnumFeature)
def test_entry_as_bytes(self):
# Expectation: Get EnumEntry as encoded byte sequence
expected = b'MultiFrame'
entry = self.feat_rw.get_entry('MultiFrame')
self.assertEqual(bytes(entry), expected)
def test_entry_as_tuple(self):
# Expectation: Get EnumEntry as (str, int)
entry = self.feat_rw.get_entry('MultiFrame')
self.assertEqual(entry.as_tuple(), self.feat_rw.get_entry(int(entry)).as_tuple())
def test_get_all_entries(self):
# Expectation: Get all possible enum entries regardless of the availability
expected = (self.feat_r.get_entry('Areascan'),)
for e in expected:
self.assertIn(e, self.feat_r.get_all_entries())
expected = (
self.feat_rw.get_entry('SingleFrame'),
self.feat_rw.get_entry('MultiFrame'),
self.feat_rw.get_entry('Continuous')
)
for e in expected:
self.assertIn(e, self.feat_rw.get_all_entries())
def test_get_avail_entries(self):
# Expectation: All returned enum entries must be available
for e in self.feat_r.get_available_entries():
self.assertTrue(e.is_available())
for e in self.feat_rw.get_available_entries():
self.assertTrue(e.is_available())
def test_get_entry_int(self):
# Expectation: Lookup a given entry by using an int as key.
# Invalid keys must return VimbaFeatureError.
expected = self.feat_r.get_all_entries()[0]
self.assertEqual(self.feat_r.get_entry(int(expected)), expected)
expected = self.feat_rw.get_all_entries()[1]
self.assertEqual(self.feat_rw.get_entry(int(expected)), expected)
self.assertRaises(VimbaFeatureError, self.feat_r.get_entry, -1)
self.assertRaises(VimbaFeatureError, self.feat_rw.get_entry, -1)
def test_get_entry_str(self):
# Expectation: Lookup a given entry by using a str as key.
# Invalid keys must return VimbaFeatureError.
expected = self.feat_r.get_all_entries()[0]
self.assertEqual(self.feat_r.get_entry(str(expected)), expected)
expected = self.feat_rw.get_all_entries()[1]
self.assertEqual(self.feat_rw.get_entry(str(expected)), expected)
self.assertRaises(VimbaFeatureError, self.feat_r.get_entry, 'Should be invalid')
self.assertRaises(VimbaFeatureError, self.feat_rw.get_entry, 'Should be invalid')
def test_get(self):
# Expectation: Get must return the current value.
self.assertNoRaise(self.feat_r.get)
self.assertNoRaise(self.feat_rw.get)
def test_set_entry(self):
# Expectation: Set given enum entry if feature is writable.
# Raises:
# - VimbaFeatureError if enum entry is from other enum feature.
# - VimbaFeatureError if feature is read only
# Read Only Feature
entry = self.feat_r.get_all_entries()[0]
self.assertRaises(VimbaFeatureError, self.feat_r.set, entry)
# Read/Write Feature
old_entry = self.feat_rw.get()
try:
# Normal operation
self.assertNoRaise(self.feat_rw.set, self.feat_rw.get_entry(2))
self.assertEqual(self.feat_rw.get(), self.feat_rw.get_entry(2))
# Provoke FeatureError by setting the feature from the ReadOnly entry.
self.assertRaises(VimbaFeatureError, self.feat_rw.set, entry)
finally:
self.feat_rw.set(old_entry)
def test_set_str(self):
# Expectation: Set given enum entry string value if feature is writable.
# Raises:
# - VimbaFeatureError if given string is not associated with this feature.
# - VimbaFeatureError if feature is read only
# Read Only Feature
self.assertRaises(VimbaFeatureError, self.feat_r.set, str(self.feat_r.get_entry(0)))
# Read/Write Feature
old_entry = self.feat_rw.get()
try:
# Normal operation
self.assertNoRaise(self.feat_rw.set, str(self.feat_rw.get_entry(2)))
self.assertEqual(self.feat_rw.get(), self.feat_rw.get_entry(2))
# Provoke FeatureError by an invalid enum value
self.assertRaises(VimbaFeatureError, self.feat_rw.set, 'Hopefully invalid')
finally:
self.feat_rw.set(old_entry)
def test_set_int(self):
# Expectation: Set given enum entry int value if feature is writable.
# Raises:
# - VimbaFeatureError if given int is not associated with this feature.
# - VimbaFeatureError if feature is read only
# Read Only Feature
self.assertRaises(VimbaFeatureError, self.feat_r.set, int(self.feat_r.get_entry(0)))
# Read/Write Feature
old_entry = self.feat_rw.get()
try:
# Normal operation
self.assertNoRaise(self.feat_rw.set, int(self.feat_rw.get_entry(2)))
self.assertEqual(self.feat_rw.get(), self.feat_rw.get_entry(2))
# Provoke FeatureError by an invalid enum value
self.assertRaises(VimbaFeatureError, self.feat_rw.set, -23)
finally:
self.feat_rw.set(old_entry)
def test_set_in_callback(self):
# Expected behavior: A set operation within a change handler must
# Raise a VimbaFeatureError to prevent an endless handler execution.
class Handler:
def __init__(self):
self.raised = False
self.event = threading.Event()
def __call__(self, feat):
try:
feat.set(feat.get())
except VimbaFeatureError:
self.raised = True
self.event.set()
old_entry = self.feat_rw.get()
try:
handler = Handler()
self.feat_rw.register_change_handler(handler)
# Trigger change handler and wait for callback execution.
self.feat_rw.set(self.feat_rw.get())
handler.event.wait()
self.assertTrue(handler.raised)
finally:
self.feat_rw.unregister_change_handler(handler)
self.feat_rw.set(old_entry)
class CamFloatFeatureTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
self.vimba._startup()
try:
self.cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to lookup Camera.') from e
try:
self.cam._open()
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to open Camera.') from e
try:
self.feat_r = self.vimba.get_feature_by_name('Elapsed')
except VimbaFeatureError:
self.cam._close()
self.vimba._shutdown()
self.skipTest('Required Feature \'Elapsed\' not available.')
try:
self.feat_rw = self.cam.get_feature_by_name('ExposureTime')
except VimbaFeatureError:
# Some Cameras name ExposureTime as ExposureTimeAbs
try:
self.feat_rw = self.cam.get_feature_by_name('ExposureTimeAbs')
except VimbaFeatureError:
self.cam._close()
self.vimba._shutdown()
self.skipTest('Required Feature \'ExposureTime\' not available.')
def tearDown(self):
self.cam._close()
self.vimba._shutdown()
def test_get_type(self):
# Expectation: FloatFeature returns FloatFeature on get_type.
self.assertEqual(self.feat_r.get_type(), FloatFeature)
self.assertEqual(self.feat_rw.get_type(), FloatFeature)
def test_get(self):
# Expectation: Get current value.
self.assertNoRaise(self.feat_r.get)
self.assertNoRaise(self.feat_rw.get)
def test_get_range(self):
# Expectation: Get value range. Raise VimbaFeatureError on non-read access.
self.assertNoRaise(self.feat_r.get_range)
self.assertNoRaise(self.feat_rw.get_range)
def test_get_increment(self):
# Expectation: Get value increment if existing. If this Feature has no
# increment, None is returned.
self.assertNoRaise(self.feat_r.get_increment)
self.assertNoRaise(self.feat_rw.get_increment)
def test_set(self):
# Expectation: Set value. Errors:
# VimbaFeatureError if access right are not writable
# VimbaFeatureError if value is out of bounds
# Read only feature
self.assertRaises(VimbaFeatureError, self.feat_r.set, 0.0)
# Read/Write Feature
old_value = self.feat_rw.get()
try:
delta = 0.1
# Range test
min_, max_ = self.feat_rw.get_range()
# Within bounds (no error)
self.assertNoRaise(self.feat_rw.set, min_)
self.assertAlmostEqual(self.feat_rw.get(), min_)
self.assertNoRaise(self.feat_rw.set, max_)
self.assertAlmostEqual(self.feat_rw.get(), max_)
# Out of bounds (must raise)
self.assertRaises(VimbaFeatureError, self.feat_rw.set, min_ - delta)
self.assertRaises(VimbaFeatureError, self.feat_rw.set, max_ + delta)
finally:
self.feat_rw.set(old_value)
def test_set_in_callback(self):
# Expectation: Calling set within change_handler must raise an VimbaFeatureError
class Handler:
def __init__(self):
self.raised = False
self.event = threading.Event()
def __call__(self, feat):
try:
feat.set(feat.get())
except VimbaFeatureError:
self.raised = True
self.event.set()
old_entry = self.feat_rw.get()
try:
handler = Handler()
self.feat_rw.register_change_handler(handler)
# Trigger change handler and wait for callback execution.
self.feat_rw.set(self.feat_rw.get())
handler.event.wait()
self.assertTrue(handler.raised)
finally:
self.feat_rw.unregister_change_handler(handler)
self.feat_rw.set(old_entry)
class CamIntFeatureTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
self.vimba._startup()
try:
self.cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to lookup Camera.') from e
try:
self.cam._open()
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to open Camera.') from e
try:
self.feat_r = self.cam.get_feature_by_name('HeightMax')
except VimbaFeatureError:
self.cam._close()
self.vimba._shutdown()
self.skipTest('Required Feature \'HeightMax\' not available.')
try:
self.feat_rw = self.cam.get_feature_by_name('Height')
except VimbaFeatureError:
self.cam._close()
self.vimba._shutdown()
self.skipTest('Required Feature \'Height\' not available.')
def tearDown(self):
self.cam._close()
self.vimba._shutdown()
def test_get_type(self):
# Expectation: IntFeature must return IntFeature on get_type
self.assertEqual(self.feat_r.get_type(), IntFeature)
self.assertEqual(self.feat_rw.get_type(), IntFeature)
def test_get(self):
# Expectation: Get current value
self.assertNoRaise(self.feat_r.get)
self.assertNoRaise(self.feat_rw.get)
def test_get_range(self):
# Expectation: Get range of accepted values
self.assertNoRaise(self.feat_r.get_range)
self.assertNoRaise(self.feat_rw.get_range)
def test_get_increment(self):
# Expectation: Get step between valid values
self.assertNoRaise(self.feat_r.get_increment)
self.assertNoRaise(self.feat_rw.get_increment)
def test_set(self):
# Expectation: Set value or raise VimbaFeatureError under the following conditions.
# 1) Invalid Access Rights
# 2) Misaligned value.
# 3) Out-of-bounds Access
# Read only feature
self.assertRaises(VimbaFeatureError, self.feat_r.set, 0)
# Writable feature
old_value = self.feat_rw.get()
try:
inc = self.feat_rw.get_increment()
min_, max_ = self.feat_rw.get_range()
# Normal usage
self.assertNoRaise(self.feat_rw.set, min_)
self.assertEqual(self.feat_rw.get(), min_)
self.assertNoRaise(self.feat_rw.set, max_)
self.assertEqual(self.feat_rw.get(), max_)
# Out of bounds access.
self.assertRaises(VimbaFeatureError, self.feat_rw.set, min_ - inc)
self.assertRaises(VimbaFeatureError, self.feat_rw.set, max_ + inc)
finally:
self.feat_rw.set(old_value)
def test_set_in_callback(self):
# Expectation: Setting a value within a Callback must raise a VimbaFeatureError
class Handler:
def __init__(self):
self.raised = False
self.event = threading.Event()
def __call__(self, feat):
try:
feat.set(feat.get())
except VimbaFeatureError:
self.raised = True
self.event.set()
old_entry = self.feat_rw.get()
try:
handler = Handler()
self.feat_rw.register_change_handler(handler)
# Trigger change handler and wait for callback execution.
min_, _ = self.feat_rw.get_range()
inc = self.feat_rw.get_increment()
if min_ <= (old_entry - inc):
self.feat_rw.set(old_entry - inc)
else:
self.feat_rw.set(old_entry + inc)
handler.event.wait()
self.assertTrue(handler.raised)
finally:
self.feat_rw.unregister_change_handler(handler)
self.feat_rw.set(old_entry)
class CamStringFeatureTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
self.vimba._startup()
try:
self.cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to lookup Camera.') from e
try:
self.cam._open()
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to open Camera.') from e
self.feat_r = None
feats = self.cam.get_features_by_type(StringFeature)
for feat in feats:
if feat.get_access_mode() == (True, False):
self.feat_r = feat
if self.feat_r is None:
self.cam._close()
self.vimba._shutdown()
self.skipTest('Test requires read only StringFeature.')
self.feat_rw = None
feats = self.cam.get_features_by_type(StringFeature)
for feat in feats:
if feat.get_access_mode() == (True, True):
self.feat_rw = feat
if self.feat_rw is None:
self.cam._close()
self.vimba._shutdown()
self.skipTest('Test requires read/write StringFeature.')
def tearDown(self):
self.cam._close()
self.vimba._shutdown()
def test_get_type(self):
# Expectation: StringFeature must return StringFeature on get_type
self.assertEqual(self.feat_r.get_type(), StringFeature)
self.assertEqual(self.feat_rw.get_type(), StringFeature)
def test_get(self):
# Expectation: Get current value without raising an exception
self.assertNoRaise(self.feat_r.get)
self.assertNoRaise(self.feat_rw.get)
def test_get_max_length(self):
# Expectation: Get maximum string length
self.assertNoRaise(self.feat_r.get_max_length)
self.assertNoRaise(self.feat_rw.get_max_length)
def test_set(self):
# Expectation:
# 1) Setting a read only feature must raise a VimbaFeatureError
# 2) Setting a read/wrtie must raise VimbaFeatureError if the string is
# longer than max length
# 3) Setting a read/write feature must work if string is long enough
# Ensure Expectation 1
self.assertRaises(VimbaFeatureError, self.feat_r.set, self.feat_r.get())
self.assertNoRaise(self.feat_rw.set, self.feat_rw.get())
# Ensure Expectation 2
old_val = self.feat_rw.get()
try:
invalid = 'a' * self.feat_rw.get_max_length()
self.assertRaises(VimbaFeatureError, self.feat_rw.set, invalid)
finally:
self.feat_rw.set(old_val)
# Ensure Expectation 3
try:
valid = 'a' * (self.feat_rw.get_max_length() - 1)
self.assertNoRaise(self.feat_rw.set, valid)
self.assertEqual(valid, self.feat_rw.get())
finally:
self.feat_rw.set(old_val)
def test_set_in_callback(self):
# Expectation: Setting a value within a Callback must raise a VimbaFeatureError
class Handler:
def __init__(self):
self.raised = False
self.event = threading.Event()
def __call__(self, feat):
try:
feat.set(feat.get())
except VimbaFeatureError:
self.raised = True
self.event.set()
try:
handler = Handler()
self.feat_rw.register_change_handler(handler)
self.feat_rw.set(self.feat_rw.get())
handler.event.wait()
self.assertTrue(handler.raised)
finally:
self.feat_rw.unregister_change_handler(handler)

View File

@@ -0,0 +1,206 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
import copy
import ctypes
from vimba import *
from vimba.frame import *
class CamFrameTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
self.vimba._startup()
try:
self.cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to lookup Camera.') from e
def tearDown(self):
self.vimba._shutdown()
def test_verify_buffer(self):
# Expectation: A Frame buffer shall have exactly the specified size on construction.
# Allocation is performed by VimbaPython
self.assertEqual(Frame(0, AllocationMode.AnnounceFrame).get_buffer_size(), 0)
self.assertEqual(Frame(1024, AllocationMode.AnnounceFrame).get_buffer_size(), 1024)
self.assertEqual(Frame(1024 * 1024, AllocationMode.AnnounceFrame).get_buffer_size(),
1024 * 1024)
def test_verify_no_copy_empty_buffer_access(self):
# Expectation: Accessing the internal buffer must not create a copy
# frame._buffer is only set on construction if buffer is allocated by VimbaPython
frame = Frame(10, AllocationMode.AnnounceFrame)
self.assertEqual(id(frame._buffer), id(frame.get_buffer()))
def test_verify_no_copy_filled_buffer_access(self):
# Expectation: Accessing the internal buffer must not create a copy
for allocation_mode in AllocationMode:
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
with self.cam:
frame = self.cam.get_frame(allocation_mode=allocation_mode)
self.assertEqual(id(frame._buffer), id(frame.get_buffer()))
def test_get_id(self):
# Expectation: get_id() must return None if Its locally constructed
# else it must return the frame id.
for allocation_mode in AllocationMode:
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
self.assertIsNone(Frame(0, allocation_mode).get_id())
with self.cam:
self.assertIsNotNone(
self.cam.get_frame(allocation_mode=allocation_mode).get_id())
def test_get_timestamp(self):
# Expectation: get_timestamp() must return None if Its locally constructed
# else it must return the timestamp.
for allocation_mode in AllocationMode:
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
self.assertIsNone(Frame(0, allocation_mode).get_timestamp())
with self.cam:
self.assertIsNotNone(
self.cam.get_frame(allocation_mode=allocation_mode).get_timestamp())
def test_get_offset(self):
# Expectation: get_offset_x() must return None if Its locally constructed
# else it must return the offset as int. Same goes for get_offset_y()
for allocation_mode in AllocationMode:
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
self.assertIsNone(Frame(0, allocation_mode).get_offset_x())
self.assertIsNone(Frame(0, allocation_mode).get_offset_y())
with self.cam:
frame = self.cam.get_frame(allocation_mode=allocation_mode)
self.assertIsNotNone(frame.get_offset_x())
self.assertIsNotNone(frame.get_offset_y())
def test_get_dimension(self):
# Expectation: get_width() must return None if Its locally constructed
# else it must return the offset as int. Same goes for get_height()
for allocation_mode in AllocationMode:
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
self.assertIsNone(Frame(0, allocation_mode).get_width())
self.assertIsNone(Frame(0, allocation_mode).get_height())
with self.cam:
frame = self.cam.get_frame(allocation_mode=allocation_mode)
self.assertIsNotNone(frame.get_width())
self.assertIsNotNone(frame.get_height())
def test_get_image_size(self):
# Expectation: get_image_size() must return 0 if locally constructed
# else it must return the image_size as int.
for allocation_mode in AllocationMode:
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
self.assertEquals(Frame(0, allocation_mode).get_image_size(), 0)
with self.cam:
self.assertNotEquals(
self.cam.get_frame(allocation_mode=allocation_mode).get_image_size(), 0)
def test_deepcopy(self):
# Expectation: a deepcopy must clone the frame buffer with its contents an
# update the internally store pointer in VmbFrame struct.
for allocation_mode in AllocationMode:
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
with self.cam:
frame = self.cam.get_frame(allocation_mode=allocation_mode)
frame_cpy = copy.deepcopy(frame)
# Ensure frames and their members are not the same object
self.assertNotEquals(id(frame), id(frame_cpy))
self.assertNotEquals(id(frame._buffer), id(frame_cpy._buffer))
self.assertNotEquals(id(frame._frame), id(frame_cpy._frame))
# Ensure that both buffers have the same size and contain the same data.
self.assertEquals(frame.get_buffer_size(), frame_cpy.get_buffer_size())
self.assertTrue(all(a == b for a, b in zip(frame.get_buffer(),
frame_cpy.get_buffer())))
# Ensure that internal Frame Pointer points to correct buffer.
self.assertEquals(frame._frame.buffer,
ctypes.cast(frame._buffer, ctypes.c_void_p).value)
self.assertEquals(frame_cpy._frame.buffer,
ctypes.cast(frame_cpy._buffer, ctypes.c_void_p).value)
self.assertEquals(frame._frame.bufferSize, frame_cpy._frame.bufferSize)
def test_get_pixel_format(self):
# Expectation: Frames have an image format set after acquisition
for allocation_mode in AllocationMode:
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
with self.cam:
self.assertNotEquals(
self.cam.get_frame(allocation_mode=allocation_mode).get_pixel_format(), 0)
def test_incompatible_formats_value_error(self):
# Expectation: Conversion into incompatible formats must lead to an value error
for allocation_mode in AllocationMode:
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
with self.cam:
frame = self.cam.get_frame(allocation_mode=allocation_mode)
current_fmt = frame.get_pixel_format()
convertable_fmt = current_fmt.get_convertible_formats()
for fmt in PixelFormat.__members__.values():
if (fmt != current_fmt) and (fmt not in convertable_fmt):
self.assertRaises(ValueError, frame.convert_pixel_format, fmt)
def test_convert_to_all_given_formats(self):
# Expectation: A Series of Frame, each acquired with a different Pixel format
# Must be convertible to all formats the given format claims its convertible to without any
# errors.
test_frames = []
with self.cam:
for fmt in self.cam.get_pixel_formats():
self.cam.set_pixel_format(fmt)
frame = self.cam.get_frame()
self.assertEqual(fmt, frame.get_pixel_format())
test_frames.append(frame)
for frame in test_frames:
# The test shall work on a copy to keep the original Frame untouched
for expected_fmt in frame.get_pixel_format().get_convertible_formats():
cpy_frame = copy.deepcopy(frame)
cpy_frame.convert_pixel_format(expected_fmt)
self.assertEquals(expected_fmt, cpy_frame.get_pixel_format())

View File

@@ -0,0 +1,149 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
import ipaddress
import struct
from vimba import *
class CamVimbaTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
def tearDown(self):
pass
def test_context_entry_exit(self):
# Expected Behavior:
# On entering the context features, cameras and interfaces shall
# be detected and after leaving the context, everything should be reverted.
self.assertRaises(RuntimeError, self.vimba.get_all_features)
self.assertRaises(RuntimeError, self.vimba.get_all_interfaces)
self.assertRaises(RuntimeError, self.vimba.get_all_cameras)
with self.vimba:
self.assertNotEqual(self.vimba.get_all_features(), ())
self.assertNotEqual(self.vimba.get_all_interfaces(), ())
self.assertNotEqual(self.vimba.get_all_cameras(), ())
self.assertRaises(RuntimeError, self.vimba.get_all_features)
self.assertRaises(RuntimeError, self.vimba.get_all_interfaces)
self.assertRaises(RuntimeError, self.vimba.get_all_cameras)
def test_get_all_interfaces(self):
# Expected Behavior: get_all_interfaces() must raise an RuntimeError in closed state and
# be non-empty then opened.
self.assertRaises(RuntimeError, self.vimba.get_all_interfaces)
with self.vimba:
self.assertTrue(self.vimba.get_all_interfaces())
def test_get_interface_by_id(self):
# Expected Behavior: All detected Interfaces must be lookup able by their Id.
# If outside of given scope, an error must be returned
with self.vimba:
ids = [inter.get_id() for inter in self.vimba.get_all_interfaces()]
for id_ in ids:
self.assertNoRaise(self.vimba.get_interface_by_id, id_)
for id_ in ids:
self.assertRaises(RuntimeError, self.vimba.get_interface_by_id, id_)
def test_get_all_cameras(self):
# Expected Behavior: get_all_cameras() must only return camera handles on a open camera.
self.assertRaises(RuntimeError, self.vimba.get_all_cameras)
with self.vimba:
self.assertTrue(self.vimba.get_all_cameras())
def test_get_camera_by_id(self):
# Expected Behavior: Lookup of test camera must not fail after system opening
camera_id = self.get_test_camera_id()
with self.vimba:
self.assertNoRaise(self.vimba.get_camera_by_id, camera_id)
def test_get_camera_by_ip(self):
# Expected Behavior: get_camera_by_id() must work with a valid ipv4 address.
# A with lookup of an invalid ipv4 address (no Camera attached)
# must raise a VimbaCameraError, a lookup with an ipv6 address must raise a
# VimbaCameraError in general (VimbaC doesn't support ipv6)
with self.vimba:
# Verify that the Test Camera is a GigE - Camera
cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
inter = self.vimba.get_interface_by_id(cam.get_interface_id())
if inter.get_type() != InterfaceType.Ethernet:
raise self.skipTest('Test requires GigE - Camera.')
# Lookup test cameras IP address.
with cam:
ip_as_number = cam.get_feature_by_name('GevCurrentIPAddress').get()
# Swap byte order, the raw value does not seem to follow network byte order.
ip_as_number = struct.pack('<L', ip_as_number)
# Verify that lookup with IPv4 Address returns the same Camera Object
ip_addr = str(ipaddress.IPv4Address(ip_as_number))
self.assertEqual(self.vimba.get_camera_by_id(ip_addr), cam)
# Verify that a lookup with an invalid IPv4 Address raises a VimbaCameraError
ip_addr = str(ipaddress.IPv4Address('127.0.0.1'))
self.assertRaises(VimbaCameraError, self.vimba.get_camera_by_id, ip_addr)
# Verify that a lookup with an IPv6 Address raises a VimbaCameraError
ip_addr = str(ipaddress.IPv6Address('FD00::DEAD:BEEF'))
self.assertRaises(VimbaCameraError, self.vimba.get_camera_by_id, ip_addr)
def test_get_camera_by_mac(self):
# Expected Behavior: get_feature_by_id must be usable with a given MAC Address.
with self.vimba:
# Verify that the Test Camera is a GigE - Camera
cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
inter = self.vimba.get_interface_by_id(cam.get_interface_id())
if inter.get_type() != InterfaceType.Ethernet:
raise self.skipTest('Test requires GigE - Camera.')
# Lookup test cameras MAC Address.
with cam:
# Construct MAC Address from raw value.
mac_as_number = cam.get_feature_by_name('GevDeviceMACAddress').get()
mac_as_bytes = mac_as_number.to_bytes(6, byteorder='big')
mac_as_str = ''.join(format(s, '02x') for s in mac_as_bytes).upper()
# Verify that lookup with MAC Address returns the same Camera Object
self.assertEqual(self.vimba.get_camera_by_id(mac_as_str), cam)
# Verify that a lookup with an invalid MAC Address raises a VimbaCameraError
invalid_mac = 'ffffffff'
self.assertRaises(VimbaCameraError, self.vimba.get_camera_by_id, invalid_mac)

View File

@@ -0,0 +1,177 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
import docopt
import sys
import os
# Add local directory to search path for test module import in this script.
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))
# Add vimba module at the start of the search path. The tests should run against the
# local VimbaPython sources regardless of any existing installations.
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
# Inject 'assertNotRaise' to default test module. Tests are derived from this class.
def _assertNoRaise(self, func, *args, **kwargs):
try:
func(*args, **kwargs)
except BaseException as e:
self.fail('Function raised: {}'.format(e))
# Inject shared test camera id into the base TestCase
def _get_test_camera_id(self) -> str:
return unittest.TestCase.test_cam_id
def _set_test_camera_id(test_cam_id) -> str:
unittest.TestCase.test_cam_id = test_cam_id
unittest.TestCase.assertNoRaise = _assertNoRaise
unittest.TestCase.set_test_camera_id = _set_test_camera_id
unittest.TestCase.get_test_camera_id = _get_test_camera_id
def _blacklist_tests(test_suite, blacklist):
for test in test_suite:
# Process TestSuites recursively
if type(test) == unittest.TestSuite:
_blacklist_tests(test, blacklist)
# Test is actually a TestCase. Add skip decorator to test
# function if the test is blacklisted.
else:
name = test._testMethodName
if name in blacklist:
setattr(test, name, unittest.skip('Blacklisted')(getattr(test, name)))
return test_suite
def main():
CLI = """VimbaPython test runner.
Usage:
runner.py -h
runner.py -s basic -o console [BLACKLIST...]
runner.py -s basic -o junit_xml REPORT_DIR [BLACKLIST...]
runner.py -s (real_cam | all) -c CAMERA_ID -o console [BLACKLIST...]
runner.py -s (real_cam | all) -c CAMERA_ID -o junit_xml REPORT_DIR [BLACKLIST...]
Arguments:
CAMERA_ID Camera Id from Camera that shall be used during testing
REPORT_DIR Directory used for junit_export.
BLACKLIST Optional sequence of unittest functions to skip.
Options:
-h Show this screen.
-s Testsuite to execute. real_cam and all require a camera to
run tests against, therefore -c is mandatory.
-c Camera Id used while testing.
-o Test output: Either console or junit_xml.
"""
args = docopt.docopt(CLI)
loader = unittest.TestLoader()
if args['CAMERA_ID']:
unittest.TestCase.set_test_camera_id(args['CAMERA_ID'])
else:
unittest.TestCase.set_test_camera_id(None)
# Select TestRunner
if args['console']:
runner = unittest.TextTestRunner(verbosity=2)
elif args['junit_xml']:
import xmlrunner
runner = xmlrunner.XMLTestRunner(output=args['REPORT_DIR'])
# Import tests cases
import basic_tests.c_binding_test
import basic_tests.util_runtime_type_check_test
import basic_tests.util_tracer_test
import basic_tests.util_context_decorator_test
import basic_tests.vimba_common_test
import basic_tests.vimba_test
import basic_tests.interface_test
import real_cam_tests.vimba_test
import real_cam_tests.feature_test
import real_cam_tests.camera_test
import real_cam_tests.frame_test
import real_cam_tests.ancillary_data_test
# Assign test cases to test suites
BASIC_TEST_MODS = [
basic_tests.c_binding_test,
basic_tests.util_runtime_type_check_test,
basic_tests.util_tracer_test,
basic_tests.util_context_decorator_test,
basic_tests.vimba_common_test,
basic_tests.vimba_test,
basic_tests.interface_test
]
REAL_CAM_TEST_MODS = [
real_cam_tests.vimba_test,
real_cam_tests.feature_test,
real_cam_tests.camera_test,
real_cam_tests.frame_test,
real_cam_tests.ancillary_data_test
]
# Prepare TestSuites
suite_basic = unittest.TestSuite()
suite_cam = unittest.TestSuite()
for mod in BASIC_TEST_MODS:
suite_basic.addTests(_blacklist_tests(loader.loadTestsFromModule(mod), args['BLACKLIST']))
for mod in REAL_CAM_TEST_MODS:
suite_cam.addTests(_blacklist_tests(loader.loadTestsFromModule(mod), args['BLACKLIST']))
# Execute TestSuites
if args['basic']:
runner.run(suite_basic)
elif args['real_cam']:
runner.run(suite_cam)
elif args['all']:
runner.run(suite_basic)
runner.run(suite_cam)
if __name__ == '__main__':
main()