EyeTrackVR/tests/test_osc_v1_params.py
Lorow 7e41a65aac
Feature/etvr module support (#106)
* initial changes

* Mostly clean up, refactor registering listeners to make sense, backport tests

* Add initial implementation of VRCFTModuleSender

* Add basic GUI for the modules settings

* Fix tooltip descriptions

# TODO:
# - there's ghosts in the machine - vrc osc is not working properly
# - min/maxing will require field combinators in the modules lmao

* Fix type validation bugs, fix typos

# TODO:
# - there's ghosts in the machine - vrc osc is not working properly
# - min/maxing will require field combinators in the modules lmao

* Add checkbox to switch to ETVR Module

# TODO:
# - there's ghosts in the machine - vrc osc is not working properly
# - min/maxing will require field combinators in the modules lmao

* Black stuff

# TODO:
# - there's ghosts in the machine - vrc osc is not working properly
# - min/maxing will require field combinators in the modules lmao

* Remove coverage by default

# TODO:
# - there's ghosts in the machine - vrc osc is not working properly
# - min/maxing will require field combinators in the modules lmao

* Fix timeout in tests

# TODO:
# - there's ghosts in the machine - vrc osc is not working properly
# - min/maxing will require field combinators in the modules lmao

* HEAVY WIP: Refactor native output,

NOTE:

I brought back the entire old OSC implementation as a live reference, this will be removed once I'm done.

This also lays ground for other modes as they're pretty similar

# TODO:
# - there's ghosts in the machine - vrc osc is not working properly
# - min/maxing will require field combinators in the modules lmao

* HEAVY WIP: Refactor v1 params output,

# TODO:
# - min/maxing will require field combinators in the modules lmao

* HEAVY WIP: Refactor v2 params output

# TODO:
# - min/maxing will require field combinators in the modules lmao

* Finish refactoring v2 and v1, fixup tests, refactor native

# TODO:
# - min/maxing will require field combinators in the modules lmao

* Add tests for v1 params

# TODO:
# - min/maxing will require field combinators in the modules lmao

* Add tests for native params

# TODO:
# - min/maxing will require field combinators in the modules lmao

* Fix OSC not getting up after config reset. Remove reset command, config sends everything changed anyway, sunset the idea of using single client and thus simplify the code a bit

# TODO:
# - min/maxing will require field combinators in the modules lmao

* Rename gui_PortNumber to gui_VRCFTModulePort for readability

# TODO:
# - min/maxing will require field combinators in the modules lmao

* Cleanup EyeID usage

# TODO:
# - min/maxing will require field combinators in the modules lmao

* Cleanup osc after rebase

# TODO:
# - min/maxing will require field combinators in the modules lmao

* Make VRChatOSCSender a bit more readable

# TODO:
# - min/maxing will require field combinators in the modules lmao

* Remove unsued VRChatOSCReceiver, this is taken care of by generic OSCReceiver

# TODO:
# - min/maxing will require field combinators in the modules lmao

* Commit crimes with try_convert_to_float to make osc, pysimplegui and pydantic happy

* Cleanup after merge

* Disable emulation by default

* Fix OSCReceiver crashing on unknown addresses

* Adjust VRCFT Module settings to look better in game

* Fix recalibrate and recenter for OSC only working for the right eye

* Fix save and restart button not restarting the tracking

* Fix broken tracking on v1 params for eye_x, clean up implementation

* Fix regular value being passed to OSC listeners instead of OSCMessage

* Add a TODO, probably to be ignored

* Add support for custom ETVR VRCFT Module listening address
2024-07-04 20:51:19 +02:00

275 lines
9.5 KiB
Python

from queue import Queue
from time import sleep
from unittest import mock
import pytest
from osc.osc import OSCManager, OSCMessage
from osc.OSCMessage import OSCMessageType
from tests import EyeInfoMock, SimpleUDPClientMock
@pytest.mark.parametrize(
"messages,expected_outcome",
[
(
[
OSCMessage(
type=OSCMessageType.EYE_INFO,
data=(
0,
EyeInfoMock(
x=0,
y=0,
blink=1,
pupil_dilation=0,
avg_velocity=0,
),
),
),
],
[
("/avatar/parameters/LeftEyeX", 0),
("/avatar/parameters/RightEyeX", 0),
("/avatar/parameters/EyesY", 0),
("/avatar/parameters/LeftEyeLidExpandedSqueeze", 1.0),
("/avatar/parameters/RightEyeLidExpandedSqueeze", 1.0),
],
),
(
[
OSCMessage(
type=OSCMessageType.EYE_INFO,
data=(
1,
EyeInfoMock(
x=0,
y=0,
blink=1,
pupil_dilation=0,
avg_velocity=0,
),
),
),
],
[
("/avatar/parameters/LeftEyeX", 0),
("/avatar/parameters/RightEyeX", 0),
("/avatar/parameters/EyesY", 0),
("/avatar/parameters/LeftEyeLidExpandedSqueeze", 1.0),
("/avatar/parameters/RightEyeLidExpandedSqueeze", 1.0),
],
),
],
)
def test_send_command_v1_params_single_eye(main_config_v1_params, messages, expected_outcome):
with mock.patch("EyeTrackApp.osc.osc.udp_client.SimpleUDPClient", SimpleUDPClientMock):
msg_queue = Queue()
client = OSCManager(
config=main_config_v1_params,
osc_message_in_queue=msg_queue,
)
client.start()
for message in messages:
sleep(0.01)
msg_queue.put(message)
client.shutdown()
assert msg_queue.empty()
assert client.osc_sender.client.messages == expected_outcome
@pytest.mark.parametrize(
"eye_data,expected_outcome",
[
(
[
OSCMessage(
type=OSCMessageType.EYE_INFO,
data=(
0,
EyeInfoMock(
x=0,
y=0,
blink=1,
pupil_dilation=1,
avg_velocity=0,
),
),
),
OSCMessage(
type=OSCMessageType.EYE_INFO,
data=(
1,
EyeInfoMock(
x=10,
y=5,
blink=0.5,
pupil_dilation=1,
avg_velocity=0,
),
),
),
],
[
("/avatar/parameters/RightEyeLidExpandedSqueeze", 1.0),
("/avatar/parameters/RightEyeX", 0),
("/avatar/parameters/RightEyeLidExpandedSqueeze", 1.0),
("/avatar/parameters/LeftEyeLidExpandedSqueeze", 0.5),
("/avatar/parameters/LeftEyeX", 0),
("/avatar/parameters/LeftEyeLidExpandedSqueeze", 0.5),
("/avatar/parameters/EyesY", 2.5),
],
),
# binary blink
(
[
OSCMessage(
type=OSCMessageType.EYE_INFO,
data=(
0,
EyeInfoMock(
x=0,
y=0,
blink=0,
pupil_dilation=1,
avg_velocity=0,
),
),
),
OSCMessage(
type=OSCMessageType.EYE_INFO,
data=(
1,
EyeInfoMock(
x=10,
y=5,
blink=0,
pupil_dilation=1,
avg_velocity=0,
),
),
),
],
[
("/avatar/parameters/RightEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/RightEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/RightEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/RightEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/RightEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/RightEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/RightEyeX", 0),
("/avatar/parameters/RightEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/LeftEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/LeftEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/LeftEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/LeftEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/LeftEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/LeftEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/LeftEyeX", 0),
("/avatar/parameters/LeftEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/EyesY", 2.5),
],
),
],
)
def test_send_command_v1_params_dual_eye(main_config_v1_params, eye_data, expected_outcome):
main_config_v1_params.eye_display_id = 2
with mock.patch("EyeTrackApp.osc.osc.udp_client.SimpleUDPClient", SimpleUDPClientMock):
msg_queue = Queue()
client = OSCManager(
config=main_config_v1_params,
osc_message_in_queue=msg_queue,
)
client.start()
for message in eye_data:
sleep(0.01)
msg_queue.put(message)
client.shutdown()
assert msg_queue.empty()
assert client.osc_sender.client.messages == expected_outcome
@pytest.mark.parametrize(
"eye_data,expected_outcome",
[
(
[
OSCMessage(
type=OSCMessageType.EYE_INFO,
data=(
0,
EyeInfoMock(
x=0,
y=0,
blink=0,
pupil_dilation=1,
avg_velocity=0,
),
),
),
OSCMessage(
type=OSCMessageType.EYE_INFO,
data=(
1,
EyeInfoMock(
x=10,
y=5,
blink=0,
pupil_dilation=1,
avg_velocity=0,
),
),
),
],
[
("/avatar/parameters/RightEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/RightEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/RightEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/RightEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/RightEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/RightEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/RightEyeX", 0),
("/avatar/parameters/RightEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/LeftEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/LeftEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/LeftEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/LeftEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/LeftEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/LeftEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/RightEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/LeftEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/LeftEyeX", 0),
("/avatar/parameters/LeftEyeLidExpandedSqueeze", 0.0),
("/avatar/parameters/EyesY", 2.5),
],
),
],
)
def test_send_command_v1_params_eye_outer_side_falloff(main_config_v1_params, eye_data, expected_outcome):
main_config_v1_params.eye_display_id = 2
main_config_v1_params.settings.gui_outer_side_falloff = True
with mock.patch("EyeTrackApp.osc.osc.udp_client.SimpleUDPClient", SimpleUDPClientMock):
msg_queue = Queue()
client = OSCManager(
config=main_config_v1_params,
osc_message_in_queue=msg_queue,
)
client.start()
for message in eye_data:
msg_queue.put(message)
sleep(1)
client.shutdown()
assert msg_queue.empty()
assert client.osc_sender.client.messages == expected_outcome