Skip to content
Snippets Groups Projects
Commit d26d9395 authored by Philip Elson's avatar Philip Elson :snake:
Browse files

Merge branch 'simplify_enum_testing' into 'master'

Simplify enum testing & unlock JPype version 0.7.3+

See merge request scripting-tools/pyjapc!44
parents c500018e 9ac4f9ec
No related branches found
No related tags found
1 merge request!44Simplify enum testing & unlock JPype version 0.7.3+
Pipeline #1856875 failed
...@@ -101,9 +101,6 @@ tests_whl_py38_jdk8_jp07: ...@@ -101,9 +101,6 @@ tests_whl_py38_jdk8_jp07:
.base_wheel_test .base_wheel_test
variables: variables:
env_spec: python=3.8 openjdk=8 JPype1=0.7 env_spec: python=3.8 openjdk=8 JPype1=0.7
# The latest JPype 0.7 will fail currently
rules:
- allow_failure: true
tests_whl_py38_jdk11_jp0p7p1: tests_whl_py38_jdk11_jp0p7p1:
extends: extends:
...@@ -116,9 +113,6 @@ tests_whl_py38_jdk11_jp07: ...@@ -116,9 +113,6 @@ tests_whl_py38_jdk11_jp07:
.base_wheel_test .base_wheel_test
variables: variables:
env_spec: python=3.8 openjdk=11 JPype1=0.7 env_spec: python=3.8 openjdk=11 JPype1=0.7
# The latest JPype 0.7 will fail currently
rules:
- allow_failure: true
build_wheel: build_wheel:
......
...@@ -4,24 +4,6 @@ import pytest ...@@ -4,24 +4,6 @@ import pytest
import jpype as jp import jpype as jp
import pyjapc import pyjapc
from .test_enum import setup_standard_meaning_enum
@pytest.fixture
def std_meaning_enum(japc):
# Note: The standard meaning enum comes from JAPC, hence the japc fixture.
if not hasattr(jp, 'JImplements'):
pytest.skip('Enum testing not available with JPype 0.6')
return setup_standard_meaning_enum()
@pytest.fixture
def std_meaning_enum_descriptor(std_meaning_enum):
SimpleDescriptor = jp.JClass(
"cern.japc.core.spi.value.SimpleDescriptorImpl")
value_descriptor = SimpleDescriptor(
jp.JClass("cern.japc.value.ValueType").ENUM, std_meaning_enum)
return value_descriptor
@pytest.fixture @pytest.fixture
...@@ -46,6 +28,30 @@ def enumtype_byte(jvm): ...@@ -46,6 +28,30 @@ def enumtype_byte(jvm):
EnumerationRegistry.clearAll() EnumerationRegistry.clearAll()
@pytest.fixture
def std_meaning_enum_descriptor(jvm):
EnumerationRegistry = jp.JClass("cern.japc.value.spi.value.EnumerationRegistry")
EnumItemData = jp.JClass("cern.japc.value.spi.value.EnumItemData")
EnumTypeBitSize = jp.JClass("cern.japc.value.EnumTypeBitSize")
HashSet = jp.JClass("java.util.HashSet")
SimpleValueStandardMeaning = jp.JClass("cern.japc.value.SimpleValueStandardMeaning")
item_set = HashSet()
item_set.add(EnumItemData(42, "ON", SimpleValueStandardMeaning.ON, False))
item_set.add(EnumItemData(-5, "OFF", SimpleValueStandardMeaning.OFF, True))
enum_type = EnumerationRegistry.registerEnumType(
"TestEnum", EnumTypeBitSize.BYTE, item_set)
SimpleDescriptor = jp.JClass(
"cern.japc.core.spi.value.SimpleDescriptorImpl")
value_descriptor = SimpleDescriptor(
jp.JClass("cern.japc.value.ValueType").ENUM, enum_type)
yield value_descriptor
# We can't clear specific EnumTypes, so for now, just clear the whole lot.
EnumerationRegistry.clearAll()
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def jvm(): def jvm():
mgr = cmmnbuild_dep_manager.Manager('pyjapc') mgr = cmmnbuild_dep_manager.Manager('pyjapc')
......
"""
Implementation of useful tools for testing Enums.
Because of the complexity of these tools there are also some limited tests
which specifically target checking the accuracy of the implementation.
All PyJapc related enum testing must be done in the PyJapc tests.
"""
import jpype as jp
try:
from jpype import JOverride, JImplements
except ImportError:
# Unsupportable version of JPype <0.7 for enum testing.
JOverride = lambda fn: fn
import pytest
def JImplementsDeferred(*jimplements_args, **jimplements_kwargs):
def JProxyCreator(cls):
# Store the __new__ method, we are going to override it for the first
# construction of one of these cls instances. After that, we can use
# the original class's __new__ method.
cls.__unwrapped_new__ = cls.__new__
def __fast_new__(cls, *args, **kwargs):
self = cls.__unwrapped_new__(cls)
self.__init__(*args, **kwargs)
return self
def __new__(cls, *args, **kwargs):
# JImplements modifies the class's __init__ in-place.
if getattr(cls, '__jimplements_called__', None) is None:
JImplements(*jimplements_args, **jimplements_kwargs)(cls)
cls.__jimplements_called__ = True
# Now put the old __new__ constructor back on this class (as
# this doesn't need to be called again).
cls.__new__ = cls.__fast_new__
return cls.__new__(cls, *args, **kwargs)
cls.__fast_new__ = __fast_new__
cls.__new__ = __new__
return cls
return JProxyCreator
@JImplementsDeferred("cern.japc.value.EnumType")
class EnumTypePyImpl:
# Implements interface 'cern.japc.value.EnumType'
# public interface EnumType {
# String getName();
# EnumTypeBitSize getBitSize();
# boolean isApplicableToEnumItemSet();
# EnumItem valueOf(String symbol);
# EnumItem valueOf(long code);
# Set<EnumItem> values();
# Collection<String> symbols();
# }
def __init__(self, name, bit_representation, enum_item_data):
self.name = name
self.bit_representation = bit_representation
# NOTE: enum_items must be EnumItemData instances.
self._enum_items_by_code = {item.getCode(): item for item in enum_item_data}
self._enum_items_by_symbol = {item.getSymbol(): item for item in enum_item_data}
@JOverride
def getName(self):
return jp.JString(self.name)
@JOverride
def getBitSize(self):
return self.bit_representation
@JOverride
def valueOf(self, symbol_or_code):
if isinstance(symbol_or_code, int):
if symbol_or_code not in self._enum_items_by_code:
# raise IndexError()
return None
enum_item_data = self._enum_items_by_code[symbol_or_code]
else:
enum_item_data = self._enum_items_by_symbol[symbol_or_code]
return EnumItemPyImpl(enum_item_data)
@JOverride
def values(self):
return {EnumItemPyImpl(item_item_data)
for item_item_data in self._enum_items_by_code.values()}
@JOverride
def symbols(self):
raise NotImplementedError()
@JOverride
def isApplicableToEnumItemSet(self):
raise NotImplementedError()
@JImplementsDeferred("cern.japc.value.EnumItem")
class EnumItemPyImpl:
# public interface EnumItem extends Comparable<EnumItem> {
# {
# EnumType getEnumType();
# long getCode();
# String getSymbol();
# String getSymbol(); # (japc < 7.5.1)
# SimpleValueStandardMeaning getStandardMeaning();
# boolean isSettable();
# }
def __init__(self, enum_item_data):
# A cern.japc.value.spi.value.EnumItemData instance.
self._enum_item = enum_item_data
@JOverride
def getEnumType(self):
raise NotImplemented()
@JOverride
def compareTo(self, other):
pass
@JOverride
def getCode(self):
return self._enum_item.getCode()
@JOverride
def getSymbol(self):
return self._enum_item.getSymbol()
@JOverride
def getString(self):
return self.getSymbol()
@JOverride
def getStandardMeaning(self):
return self._enum_item.getStandardMeaning()
@JOverride
def isSettable(self):
return self._enum_item.isSettable()
def setup_standard_meaning_enum():
SimpleDescriptor = jp.JClass("cern.japc.core.spi.value.SimpleDescriptorImpl")
SimpleValueStandardMeaning = jp.JClass("cern.japc.value.SimpleValueStandardMeaning")
# EnumItemData(long code, String symbol, SimpleValueStandardMeaning meaning, boolean settable)
EnumItemData = jp.JClass('cern.japc.value.spi.value.EnumItemData')
enum_item_on = EnumItemData(42, "ON", SimpleValueStandardMeaning.ON, False)
enum_item_off = EnumItemData(-5, "OFF", SimpleValueStandardMeaning.OFF, True)
EnumTypeBitSize = jp.JClass('cern.japc.value.EnumTypeBitSize')
# We implement the EnumType interface, rather than instantiating a
# EnumTypeImpl as we couldn't find a way to construct a set<EnumItem> in
# JPype.
return EnumTypePyImpl(
"SimpleValueStandardMeaning",
EnumTypeBitSize.INT,
[enum_item_on, enum_item_off])
@pytest.mark.parametrize(
"enum_code, enum_value",
(
[42, "ON"],
[-5, "OFF"],
))
def test_EnumItem_valueOf(jvm, std_meaning_enum, enum_code, enum_value):
enum_item = std_meaning_enum.valueOf(enum_code)
assert enum_item.getCode() == enum_code
assert enum_item.getSymbol() == enum_value
enum_item = std_meaning_enum.valueOf(enum_value)
assert enum_item.getCode() == enum_code
assert enum_item.getSymbol() == enum_value
...@@ -89,14 +89,19 @@ def test_convert_py_to_val_primitive(japc, jprimitive, expectation): ...@@ -89,14 +89,19 @@ def test_convert_py_to_val_primitive(japc, jprimitive, expectation):
]) ])
def test_convert_py_to_val_primitive_illegal_argument(japc, jprimitive): def test_convert_py_to_val_primitive_illegal_argument(japc, jprimitive):
jprimitive = eval(jprimitive) jprimitive = eval(jprimitive)
jp_lt_0p7p1 = jp.__version__ <= "0.7.1"
if JPYPE_LT_0p7: if JPYPE_LT_0p7:
with pytest.raises(RuntimeError, with pytest.raises(RuntimeError,
match="No matching overloads found for newValue"): match="No matching overloads found for newValue"):
japc._convertPyToVal(jprimitive) japc._convertPyToVal(jprimitive)
else: elif jp_lt_0p7p1:
with pytest.raises(jp.JClass('java.lang.IllegalArgumentException'), with pytest.raises(jp.JClass('java.lang.IllegalArgumentException'),
match="unsupported type: class java.lang.*"): match="unsupported type: class java.lang.*"):
japc._convertPyToVal(jprimitive) japc._convertPyToVal(jprimitive)
else:
with pytest.raises(TypeError,
match="Ambiguous overloads found for"):
japc._convertPyToVal(jprimitive)
@pytest.mark.parametrize( @pytest.mark.parametrize(
...@@ -122,7 +127,11 @@ def test_convert_py_to_val_roundtrip_enum( ...@@ -122,7 +127,11 @@ def test_convert_py_to_val_roundtrip_enum(
['NOT_VALID'], ['NOT_VALID'],
]) ])
def test_invalid_enum(japc, std_meaning_enum_descriptor, to_convert): def test_invalid_enum(japc, std_meaning_enum_descriptor, to_convert):
with pytest.raises(jp.java.lang.RuntimeException): if JPYPE_LT_0p7:
raises = pytest.raises(jp.JavaException)
else:
raises = pytest.raises(jp.java.lang.RuntimeException)
with raises:
japc._convertPyToVal(to_convert, std_meaning_enum_descriptor) japc._convertPyToVal(to_convert, std_meaning_enum_descriptor)
......
...@@ -17,7 +17,7 @@ with (HERE / 'README.rst').open('rt') as fh: ...@@ -17,7 +17,7 @@ with (HERE / 'README.rst').open('rt') as fh:
REQUIREMENTS: dict = { REQUIREMENTS: dict = {
'core': [ 'core': [
'jpype1>=0.6.1,!=0.7.0,!=0.7.2,<=0.7.1', 'jpype1>=0.6.1,!=0.7.0,!=0.7.2',
'cmmnbuild-dep-manager>=2.4.0', 'cmmnbuild-dep-manager>=2.4.0',
'numpy', 'numpy',
'pytz', 'pytz',
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment