From 45b0b4980d562d6cc39bc548dfcf301d69195531 Mon Sep 17 00:00:00 2001 From: harshsomankar123-tech Date: Sun, 19 Apr 2026 04:55:09 +0530 Subject: [PATCH] IDE-314: Stabilize and optimize sensor list tests with Robolectric integration test --- .../FormulaEditorSensorListTest.kt | 865 ++++-------------- .../FormulaEditorSensorListIntegrationTest.kt | 576 ++++++++++++ 2 files changed, 746 insertions(+), 695 deletions(-) create mode 100644 catroid/src/test/java/org/catrobat/catroid/test/robolectric/formulaeditor/FormulaEditorSensorListIntegrationTest.kt diff --git a/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/formulaeditor/FormulaEditorSensorListTest.kt b/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/formulaeditor/FormulaEditorSensorListTest.kt index de2ec8c64f9..e677485494f 100644 --- a/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/formulaeditor/FormulaEditorSensorListTest.kt +++ b/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/formulaeditor/FormulaEditorSensorListTest.kt @@ -1,6 +1,6 @@ /* * Catroid: An on-device visual programming system for Android devices - * Copyright (C) 2010-2025 The Catrobat Team + * Copyright (C) 2010-2026 The Catrobat Team * () * * This program is free software: you can redistribute it and/or modify @@ -55,17 +55,27 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.experimental.categories.Category -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +/** + * Slimmed-down Espresso smoke test for the Formula Editor sensor list. + * + * This test covers representative UI interaction patterns that cannot be verified + * without a real device/emulator: + * - Standard sensor selection (basic click flow) + * - Header rendering verification + * - GPS sensor selection (compute + pressBack flow) + * - NXT sensor selection (port config dialog flow) + * - EV3 sensor selection (port config dialog flow) + * - Touch sensor with parameter display + * - Face detection sensor + * - Date/Time sensor + * + * The full list of ~160 sensor items (completeness, order, naming) is verified by + * the fast Robolectric-based FormulaEditorSensorListIntegrationTest. + */ @Category(Cat.AppUi::class, Level.Smoke::class) -@RunWith(Parameterized::class) -class FormulaEditorSensorListTest( - index: String, - name: String, - param: String, - private var sensorHeader: String -) { +class FormulaEditorSensorListTest { + @Rule @JvmField var baseActivityTestRule = FragmentActivityTestRule( @@ -74,56 +84,16 @@ class FormulaEditorSensorListTest( SpriteActivity.FRAGMENT_SCRIPTS ) - private val itemIndex: Int = index.toInt() - private var sensorName: String = name + param - private var isHeader = sensorHeader.isNotBlank() - private var isGpsSensor = false - - private var sensorNumber: String? = null + @JvmField + @Rule + var permissionRule: GrantPermissionRule = GrantPermissionRule.grant( + Manifest.permission.ACCESS_FINE_LOCATION + ) private val projectName = "FormulaEditorSensorListTest" private lateinit var recyclerViewItemMatcher: RecyclerViewItemMatcher private var initialSettings = mutableMapOf() - init { - if (sensorName.contains("^(NXT|EV3)\\s?.*".toRegex())) { - sensorName = name - sensorNumber = param - } - if (sensorName.contains(str(R.string.formula_editor_sensor_longitude)) || - sensorName.contains(str(R.string.formula_editor_sensor_latitude)) || - sensorName.contains(str(R.string.formula_editor_sensor_altitude)) || - sensorName.contains(str(R.string.formula_editor_sensor_location_accuracy))) { - isGpsSensor = true - } - } - - private fun saveInitialSettings() { - val sharedPreferences = PreferenceManager - .getDefaultSharedPreferences(ApplicationProvider.getApplicationContext()) - allShowBrickSettings.forEach { setting -> - initialSettings[setting] = sharedPreferences.getBoolean(setting, false) - } - } - - private fun restoreInitialSettings() { - val sharedPreferencesEditor = PreferenceManager - .getDefaultSharedPreferences(ApplicationProvider.getApplicationContext()).edit() - allShowBrickSettings.forEach { setting -> - sharedPreferencesEditor.putBoolean(setting, initialSettings[setting]!!) - } - sharedPreferencesEditor.commit() - } - - private fun setAllShowBrickSettingsToTrue() { - val sharedPreferencesEditor = PreferenceManager - .getDefaultSharedPreferences(ApplicationProvider.getApplicationContext()).edit() - allShowBrickSettings.forEach { setting -> - sharedPreferencesEditor.putBoolean(setting, true) - } - sharedPreferencesEditor.commit() - } - @Before @kotlin.jvm.Throws(Exception::class) fun setUp() { @@ -142,675 +112,180 @@ class FormulaEditorSensorListTest( restoreInitialSettings() } - @JvmField - @Rule - var permissionRule: GrantPermissionRule = GrantPermissionRule.grant( - Manifest.permission.ACCESS_FINE_LOCATION - ) + /** + * Tests a standard sensor selection (loudness) and verifies it renders with the + * correct header for the Device Sensors section. + */ @Test - fun testSensorListElements() { - onBrickAtPosition(1).onChildView(withId(R.id.brick_change_size_by_edit_text)) - .perform(click()) + fun testSelectStandardSensorWithHeader() { + openDeviceCategory() - onFormulaEditor() - .performOpenCategory(FormulaEditorWrapper.Category.DEVICE) + val sensorName = str(R.string.formula_editor_sensor_loudness) + val headerName = str(R.string.formula_editor_device_sensors) - onView(withId(R.id.recycler_view)) - .perform(scrollToPosition(itemIndex)) - - if (isHeader) { - onView(withText(sensorHeader)) - .check( - matches( - recyclerViewItemMatcher - .withIdInsidePosition(R.id.headline, itemIndex) - ) - ) - } + // Find the loudness sensor (first item in Device Sensors section) + // We search by scrolling until we find it, then check header + name + onCategoryList().performSelect(sensorName) + onFormulaEditor().checkShows(getSelectedSensorString(sensorName)) + } - onView(withText(sensorName)) - .check( - matches( - recyclerViewItemMatcher - .withIdInsidePosition(R.id.title_view, itemIndex) - ) - ) + /** + * Tests GPS sensor selection which triggers the compute + pressBack flow. + */ + @Test + fun testSelectGpsSensor() { + openDeviceCategory() + val sensorName = str(R.string.formula_editor_sensor_latitude) onCategoryList().performSelect(sensorName) - if (isGpsSensor) { - onView(withId(R.id.formula_editor_keyboard_compute)) - .perform(click()) - pressBack() - } + onView(withId(R.id.formula_editor_keyboard_compute)) + .perform(click()) + pressBack() - sensorNumber?.let { sensor -> - val portNumber = when (sensor.trim()[sensor.length - 1]) { - '1' -> str(R.string.lego_port_1) - '2' -> str(R.string.lego_port_2) - '3' -> str(R.string.lego_port_3) - '4' -> str(R.string.lego_port_4) - else -> "Error in port number string" - } - onView(withText(StringContains(portNumber))).perform(click()) - onView(withText(R.string.ok)).perform(click()) - - onFormulaEditor().checkShows(getSelectedSensorString(sensor)) - true - } ?: onFormulaEditor().checkShows(getSelectedSensorString(sensorName)) + onFormulaEditor().checkShows(getSelectedSensorString(sensorName)) } - private fun getSelectedSensorString(functionString: String): String { - return functionString - .replace("^(.+?)\\(".toRegex(), "$1( ") - .replace(",", " , ") - .replace("-", "- ") - .replace("\\)$".toRegex(), " )") + " " + /** + * Tests NXT sensor selection which triggers the port config dialog flow. + */ + @Test + fun testSelectNxtSensorWithPortDialog() { + openDeviceCategory() + + val sensorName = str(R.string.formula_editor_sensor_lego_nxt_touch) + onCategoryList().performSelect(sensorName) + + val portNumber = str(R.string.lego_port_4) + onView(withText(StringContains(portNumber))).perform(click()) + onView(withText(R.string.ok)).perform(click()) + + val portSensorString = str(R.string.formula_editor_sensor_lego_nxt_4) + onFormulaEditor().checkShows(getSelectedSensorString(portSensorString)) } - @Suppress("LargeClass") - companion object { - private fun str(paramId: Int): String = UiTestUtils.getResourcesString(paramId) ?: "" + /** + * Tests EV3 sensor selection which triggers the port config dialog flow. + */ + @Test + fun testSelectEv3SensorWithPortDialog() { + openDeviceCategory() - @JvmStatic - @Parameterized.Parameters(name = "{3}\n{index}. {1}") - fun params() = arrayListOf>().run { - val paramsData = mutableListOf>() - paramsData.addAll(listOfNXT) - paramsData.addAll(listOfEV3) - paramsData.addAll(listOfPhiro) - paramsData.addAll(listOfArduino) - paramsData.addAll(listOfDrone) - paramsData.addAll(listOfRaspberry) - paramsData.addAll(listOfNFC) - paramsData.addAll(listOfCast) - paramsData.addAll(listOfSpeech) - paramsData.addAll(listOfFaceDetection) - paramsData.addAll(listOfPoseDetection) - paramsData.addAll(listOfTextRecognition) - paramsData.addAll(listOfDevice) - paramsData.addAll(listOfTouch) - paramsData.addAll(listOfDateTime) - - paramsData.forEachIndexed { index, sensor -> - this.add(arrayListOf(index.toString()).run { - this.addAll(sensor) - this.toTypedArray() - }) - } - - this - } + val sensorName = str(R.string.formula_editor_sensor_lego_ev3_sensor_touch) + onCategoryList().performSelect(sensorName) - private val listOfNXT = listOf( - listOf( - str(R.string.formula_editor_sensor_lego_nxt_touch), - str(R.string.formula_editor_sensor_lego_nxt_4), - str(R.string.formula_editor_device_lego_nxt) - ), - listOf( - str(R.string.formula_editor_sensor_lego_nxt_sound), - str(R.string.formula_editor_sensor_lego_nxt_1), "" - ), - listOf( - str(R.string.formula_editor_sensor_lego_nxt_light), - str(R.string.formula_editor_sensor_lego_nxt_2), "" - ), - listOf( - str(R.string.formula_editor_sensor_lego_nxt_light_active), - str(R.string.formula_editor_sensor_lego_nxt_3), "" - ), - listOf( - str(R.string.formula_editor_sensor_lego_nxt_ultrasonic), - str(R.string.formula_editor_sensor_lego_nxt_3), "" - ) - ) + val portNumber = str(R.string.lego_port_1) + onView(withText(StringContains(portNumber))).perform(click()) + onView(withText(R.string.ok)).perform(click()) - private val listOfEV3 = listOf( - listOf( - str(R.string.formula_editor_sensor_lego_ev3_sensor_touch), - str(R.string.formula_editor_sensor_lego_ev3_1), - str(R.string.formula_editor_device_lego_ev3) - ), - listOf( - str(R.string.formula_editor_sensor_lego_ev3_sensor_infrared), - str(R.string.formula_editor_sensor_lego_ev3_2), "" - ), - listOf( - str(R.string.formula_editor_sensor_lego_ev3_sensor_color), - str(R.string.formula_editor_sensor_lego_ev3_3), "" - ), - listOf( - str(R.string.formula_editor_sensor_lego_ev3_sensor_color_ambient), - str(R.string.formula_editor_sensor_lego_ev3_4), "" - ), - listOf( - str(R.string.formula_editor_sensor_lego_ev3_sensor_color_reflected), - str(R.string.formula_editor_sensor_lego_ev3_1), "" - ), - listOf( - str(R.string.formula_editor_sensor_lego_ev3_sensor_hitechnic_color), - str(R.string.formula_editor_sensor_lego_ev3_2), "" - ), - listOf( - str(R.string.formula_editor_sensor_lego_ev3_sensor_nxt_temperature_c), - str(R.string.formula_editor_sensor_lego_ev3_3), "" - ), - listOf( - str(R.string.formula_editor_sensor_lego_ev3_sensor_nxt_temperature_f), - str(R.string.formula_editor_sensor_lego_ev3_4), "" - ), - listOf( - str(R.string.formula_editor_sensor_lego_ev3_sensor_nxt_light), - str(R.string.formula_editor_sensor_lego_ev3_1), "" - ), - listOf( - str(R.string.formula_editor_sensor_lego_ev3_sensor_nxt_light_active), - str(R.string.formula_editor_sensor_lego_ev3_2), "" - ), - listOf( - str(R.string.formula_editor_sensor_lego_ev3_sensor_nxt_sound), - str(R.string.formula_editor_sensor_lego_ev3_3), "" - ), - listOf( - str(R.string.formula_editor_sensor_lego_ev3_sensor_nxt_ultrasonic), - str(R.string.formula_editor_sensor_lego_ev3_4), "" - ) - ) + val portSensorString = str(R.string.formula_editor_sensor_lego_ev3_1) + onFormulaEditor().checkShows(getSelectedSensorString(portSensorString)) + } - private val listOfPhiro = listOf( - listOf( - str(R.string.formula_editor_phiro_sensor_front_left), - "", str(R.string.formula_editor_device_phiro) - ), - listOf(str(R.string.formula_editor_phiro_sensor_front_right), "", ""), - listOf(str(R.string.formula_editor_phiro_sensor_side_left), "", ""), - listOf(str(R.string.formula_editor_phiro_sensor_side_right), "", ""), - listOf(str(R.string.formula_editor_phiro_sensor_bottom_left), "", ""), - listOf(str(R.string.formula_editor_phiro_sensor_bottom_right), "", "") - ) + /** + * Tests touch sensor with parameter display (multi-finger). + */ + @Test + fun testSelectTouchSensorWithParameter() { + openDeviceCategory() - private val listOfArduino = listOf( - listOf( - str(R.string.formula_editor_function_arduino_read_pin_value_analog), - str(R.string.formula_editor_function_pin_default_parameter), - str(R.string.formula_editor_device_arduino) - ), - listOf( - str(R.string.formula_editor_function_arduino_read_pin_value_digital), - str(R.string.formula_editor_function_pin_default_parameter), "" - ) - ) + val sensorName = str(R.string.formula_editor_function_finger_x) + + str(R.string.formula_editor_function_no_parameter) + onCategoryList().performSelect(sensorName) + onFormulaEditor().checkShows(getSelectedSensorString(sensorName)) + } - private val listOfDrone = listOf( - listOf( - str(R.string.formula_editor_sensor_drone_battery_status), - "", str(R.string.formula_editor_device_drone) - ), - listOf(str(R.string.formula_editor_sensor_drone_emergency_state), "", ""), - listOf(str(R.string.formula_editor_sensor_drone_flying), "", ""), - listOf(str(R.string.formula_editor_sensor_drone_initialized), "", ""), - listOf(str(R.string.formula_editor_sensor_drone_usb_active), "", ""), - listOf(str(R.string.formula_editor_sensor_drone_usb_remaining_time), "", ""), - listOf(str(R.string.formula_editor_sensor_drone_camera_ready), "", ""), - listOf(str(R.string.formula_editor_sensor_drone_record_ready), "", ""), - listOf(str(R.string.formula_editor_sensor_drone_recording), "", ""), - listOf(str(R.string.formula_editor_sensor_drone_num_frames), "", "") - ) + /** + * Tests face detection sensor selection. + */ + @Test + fun testSelectFaceDetectionSensor() { + openDeviceCategory() - private val listOfRaspberry = listOf( - listOf( - str(R.string.formula_editor_function_raspi_read_pin_value_digital), - str(R.string.formula_editor_function_pin_default_parameter), - str(R.string.formula_editor_device_raspberry) - ) - ) + val sensorName = str(R.string.formula_editor_sensor_face_detected) + + str(R.string.formula_editor_function_no_parameter) + onCategoryList().performSelect(sensorName) + onFormulaEditor().checkShows(getSelectedSensorString(sensorName)) + } - private val listOfNFC = listOf( - listOf( - str(R.string.formula_editor_nfc_tag_id), - "", str(R.string.formula_editor_device_nfc) - ), - listOf(str(R.string.formula_editor_nfc_tag_message), "", "") - ) + /** + * Tests date/time sensor selection. + */ + @Test + fun testSelectDateTimeSensor() { + openDeviceCategory() - private val listOfCast = listOf( - listOf( - str(R.string.formula_editor_sensor_gamepad_a_pressed), - "", str(R.string.formula_editor_device_cast) - ), - listOf(str(R.string.formula_editor_sensor_gamepad_b_pressed), "", ""), - listOf(str(R.string.formula_editor_sensor_gamepad_up_pressed), "", ""), - listOf(str(R.string.formula_editor_sensor_gamepad_down_pressed), "", ""), - listOf(str(R.string.formula_editor_sensor_gamepad_left_pressed), "", ""), - listOf(str(R.string.formula_editor_sensor_gamepad_right_pressed), "", "") - ) + val sensorName = str(R.string.formula_editor_sensor_timer) + onCategoryList().performSelect(sensorName) + onFormulaEditor().checkShows(getSelectedSensorString(sensorName)) + } - private val listOfSpeech = listOf( - listOf( - str(R.string.formula_editor_listening_language_sensor), - "", str(R.string.formula_editor_speech_recognition) - ) - ) + /** + * Tests that a header is correctly displayed for the first item in a section. + */ + @Test + fun testHeaderDisplayForNxtSection() { + openDeviceCategory() - private val listOfFaceDetection = listOf( - listOf( - str(R.string.formula_editor_sensor_face_detected), - str(R.string.formula_editor_function_no_parameter), - str(R.string.formula_editor_device_face_detection) - ), - listOf( - str(R.string.formula_editor_sensor_face_size), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_face_x_position), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_face_y_position), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_second_face_detected), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_second_face_size), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_second_face_x_position), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_second_face_y_position), - str(R.string.formula_editor_function_no_parameter), "" - ) - ) + // NXT is the first section (index 0) + val headerText = str(R.string.formula_editor_device_lego_nxt) + onView(withId(R.id.recycler_view)) + .perform(scrollToPosition(0)) - private val listOfPoseDetection = listOf( - listOf( - str(R.string.formula_editor_sensor_nose_x), - str(R.string.formula_editor_function_no_parameter), - str(R.string.formula_editor_device_pose_detection) - ), - listOf( - str(R.string.formula_editor_sensor_nose_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_eye_inner_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_eye_inner_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_eye_center_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_eye_center_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_eye_outer_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_eye_outer_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_eye_inner_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_eye_inner_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_eye_center_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_eye_center_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_eye_outer_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_eye_outer_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_ear_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_ear_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_ear_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_ear_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_mouth_left_corner_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_mouth_left_corner_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_mouth_right_corner_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_mouth_right_corner_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_shoulder_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_shoulder_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_shoulder_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_shoulder_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_elbow_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_elbow_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_elbow_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_elbow_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_wrist_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_wrist_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_wrist_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_wrist_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_pinky_knuckle_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_pinky_knuckle_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_pinky_knuckle_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_pinky_knuckle_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_index_knuckle_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_index_knuckle_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_index_knuckle_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_index_knuckle_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_thumb_knuckle_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_thumb_knuckle_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_thumb_knuckle_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_thumb_knuckle_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_hip_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_hip_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_hip_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_hip_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_knee_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_knee_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_knee_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_knee_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_ankle_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_ankle_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_ankle_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_ankle_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_heel_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_heel_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_heel_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_heel_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_foot_index_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_left_foot_index_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_foot_index_x), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_right_foot_index_y), - str(R.string.formula_editor_function_no_parameter), "" + onView(withText(headerText)) + .check( + matches( + recyclerViewItemMatcher + .withIdInsidePosition(R.id.headline, 0) + ) ) - ) + } - private val listOfTextRecognition = listOf( - listOf( - str(R.string.formula_editor_sensor_text_from_camera), - str(R.string.formula_editor_function_no_parameter), - str(R.string.formula_editor_device_text_recognition) - ), - listOf( - str(R.string.formula_editor_sensor_text_blocks_number), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_function_text_block_x), - str(R.string.formula_editor_function_text_block_parameter), "" - ), - listOf( - str(R.string.formula_editor_function_text_block_y), - str(R.string.formula_editor_function_text_block_parameter), "" - ), - listOf( - str(R.string.formula_editor_function_text_block_size), - str(R.string.formula_editor_function_text_block_parameter), "" - ), - listOf( - str(R.string.formula_editor_function_text_block_from_camera), - str(R.string.formula_editor_function_text_block_parameter), "" - ), - listOf( - str(R.string.formula_editor_function_text_block_language_from_camera), - str(R.string.formula_editor_function_text_block_parameter), "" - ) - ) + // Helper methods - private val listOfDevice = listOf( - listOf( - str(R.string.formula_editor_sensor_loudness), - "", str(R.string.formula_editor_device_sensors) - ), - listOf(str(R.string.formula_editor_function_touched), "", ""), - listOf(str(R.string.formula_editor_sensor_stage_width), "", ""), - listOf(str(R.string.formula_editor_sensor_stage_height), "", ""), - listOf( - str(R.string.formula_editor_sensor_color_at_x_y), - str(R.string.formula_editor_sensor_color_at_x_y_parameter), "" - ), - listOf( - str(R.string.formula_editor_sensor_color_equals_color), - str(R.string.formula_editor_sensor_color_equals_color_parameter), "" - ), - listOf(str(R.string.formula_editor_sensor_x_acceleration), "", ""), - listOf(str(R.string.formula_editor_sensor_y_acceleration), "", ""), - listOf(str(R.string.formula_editor_sensor_z_acceleration), "", ""), - listOf(str(R.string.formula_editor_sensor_x_inclination), "", ""), - listOf(str(R.string.formula_editor_sensor_y_inclination), "", ""), - listOf(str(R.string.formula_editor_sensor_compass_direction), "", ""), - listOf(str(R.string.formula_editor_sensor_latitude), "", ""), - listOf(str(R.string.formula_editor_sensor_longitude), "", ""), - listOf(str(R.string.formula_editor_sensor_location_accuracy), "", ""), - listOf(str(R.string.formula_editor_sensor_altitude), "", ""), - listOf(str(R.string.formula_editor_sensor_user_language), "", "") - ) + private fun openDeviceCategory() { + onBrickAtPosition(1).onChildView(withId(R.id.brick_change_size_by_edit_text)) + .perform(click()) + onFormulaEditor() + .performOpenCategory(FormulaEditorWrapper.Category.DEVICE) + } - private val listOfTouch = listOf( - listOf( - str(R.string.formula_editor_function_finger_x), - str(R.string.formula_editor_function_no_parameter), - str(R.string.formula_editor_device_touch_detection) - ), - listOf( - str(R.string.formula_editor_function_finger_y), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_function_is_finger_touching), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_function_multi_finger_x), - str(R.string.formula_editor_function_touch_parameter), "" - ), - listOf( - str(R.string.formula_editor_function_multi_finger_y), - str(R.string.formula_editor_function_touch_parameter), "" - ), - listOf( - str(R.string.formula_editor_function_is_multi_finger_touching), - str(R.string.formula_editor_function_touch_parameter), "" - ), - listOf( - str(R.string.formula_editor_function_index_of_last_finger), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_function_number_of_current_touches), - str(R.string.formula_editor_function_no_parameter), "" - ), - listOf( - str(R.string.formula_editor_function_index_of_current_touch), - str(R.string.formula_editor_function_touch_parameter), "" - ) - ) + private fun getSelectedSensorString(functionString: String): String { + return functionString + .replace("^(.+?)\\(".toRegex(), "$1( ") + .replace(",", " , ") + .replace("-", "- ") + .replace("\\)$".toRegex(), " )") + " " + } - private val listOfDateTime = listOf( - listOf( - str(R.string.formula_editor_sensor_timer), - "", str(R.string.formula_editor_device_date_and_time) - ), - listOf(str(R.string.formula_editor_sensor_date_year), "", ""), - listOf(str(R.string.formula_editor_sensor_date_month), "", ""), - listOf(str(R.string.formula_editor_sensor_date_day), "", ""), - listOf(str(R.string.formula_editor_sensor_date_weekday), "", ""), - listOf(str(R.string.formula_editor_sensor_time_hour), "", ""), - listOf(str(R.string.formula_editor_sensor_time_minute), "", ""), - listOf(str(R.string.formula_editor_sensor_time_second), "", "") - ) + private fun saveInitialSettings() { + val sharedPreferences = PreferenceManager + .getDefaultSharedPreferences(ApplicationProvider.getApplicationContext()) + allShowBrickSettings.forEach { setting -> + initialSettings[setting] = sharedPreferences.getBoolean(setting, false) + } + } + + private fun restoreInitialSettings() { + val sharedPreferencesEditor = PreferenceManager + .getDefaultSharedPreferences(ApplicationProvider.getApplicationContext()).edit() + allShowBrickSettings.forEach { setting -> + sharedPreferencesEditor.putBoolean(setting, initialSettings[setting]!!) + } + sharedPreferencesEditor.commit() + } + + private fun setAllShowBrickSettingsToTrue() { + val sharedPreferencesEditor = PreferenceManager + .getDefaultSharedPreferences(ApplicationProvider.getApplicationContext()).edit() + allShowBrickSettings.forEach { setting -> + sharedPreferencesEditor.putBoolean(setting, true) + } + sharedPreferencesEditor.commit() + } + + companion object { + private fun str(paramId: Int): String = UiTestUtils.getResourcesString(paramId) ?: "" private val allShowBrickSettings: List = listOf( SettingsFragment.SETTINGS_SHOW_ARDUINO_BRICKS, diff --git a/catroid/src/test/java/org/catrobat/catroid/test/robolectric/formulaeditor/FormulaEditorSensorListIntegrationTest.kt b/catroid/src/test/java/org/catrobat/catroid/test/robolectric/formulaeditor/FormulaEditorSensorListIntegrationTest.kt new file mode 100644 index 00000000000..54d64178efe --- /dev/null +++ b/catroid/src/test/java/org/catrobat/catroid/test/robolectric/formulaeditor/FormulaEditorSensorListIntegrationTest.kt @@ -0,0 +1,576 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2026 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * http://developer.catrobat.org/license_additional_term + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.catrobat.catroid.test.robolectric.formulaeditor + +import android.app.Activity +import android.preference.PreferenceManager +import androidx.appcompat.app.AppCompatActivity +import org.catrobat.catroid.ProjectManager +import org.catrobat.catroid.R +import org.catrobat.catroid.content.Project +import org.catrobat.catroid.content.Sprite +import org.catrobat.catroid.content.StartScript +import org.catrobat.catroid.ui.recyclerview.fragment.CategoryListItems +import org.catrobat.catroid.ui.settingsfragments.SettingsFragment +import org.catrobat.catroid.formulaeditor.SensorHandler +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.koin.core.context.loadKoinModules +import org.koin.dsl.module +import org.koin.java.KoinJavaComponent +import org.mockito.Mockito.`when` +import org.mockito.Mockito.mock +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment +import org.robolectric.annotation.Config + +/** + * Integration test that verifies the completeness, order, naming, and headers + * of all sensor items in the Formula Editor's DEVICE category. + * + * This replaces ~160 parameterized Espresso tests with a single fast + * Robolectric test that calls [CategoryListItems.getSensorItems] directly. + */ +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [28]) +class FormulaEditorSensorListIntegrationTest { + + private lateinit var activity: Activity + private var initialSettings = mutableMapOf() + + private val allShowBrickSettings: List = listOf( + SettingsFragment.SETTINGS_SHOW_ARDUINO_BRICKS, + SettingsFragment.SETTINGS_SHOW_PHIRO_BRICKS_CHECKBOX_PREFERENCE, + SettingsFragment.SETTINGS_SHOW_NFC_BRICKS, + SettingsFragment.SETTINGS_MINDSTORMS_NXT_BRICKS_CHECKBOX_PREFERENCE, + SettingsFragment.SETTINGS_MINDSTORMS_EV3_BRICKS_CHECKBOX_PREFERENCE, + SettingsFragment.SETTINGS_SHOW_PARROT_AR_DRONE_BRICKS, + SettingsFragment.SETTINGS_SHOW_RASPI_BRICKS, + SettingsFragment.SETTINGS_SHOW_PLOT_BRICKS, + SettingsFragment.SETTINGS_CAST_GLOBALLY_ENABLED, + SettingsFragment.SETTINGS_SHOW_AI_SPEECH_RECOGNITION_SENSORS, + SettingsFragment.SETTINGS_SHOW_AI_SPEECH_SYNTHETIZATION_SENSORS, + SettingsFragment.SETTINGS_SHOW_AI_FACE_DETECTION_SENSORS, + SettingsFragment.SETTINGS_SHOW_AI_POSE_DETECTION_SENSORS, + SettingsFragment.SETTINGS_SHOW_AI_TEXT_RECOGNITION_SENSORS, + SettingsFragment.SETTINGS_SHOW_AI_OBJECT_DETECTION_SENSORS + ) + + private fun str(resId: Int): String = activity.getString(resId) + + @Before + fun setUp() { + val application = RuntimeEnvironment.getApplication() + + // Settings MUST be set before activity setup + saveInitialSettings() + setAllShowBrickSettingsToTrue() + + // Mock SensorHandler to return true for all availability checks + val mockSensorHandler = mock(SensorHandler::class.java) + `when`(mockSensorHandler.accelerationAvailable()).thenReturn(true) + `when`(mockSensorHandler.inclinationAvailable()).thenReturn(true) + `when`(mockSensorHandler.compassAvailable()).thenReturn(true) + + // Inject into Koin (for components that use injection) + loadKoinModules(module { + single(override = true) { mockSensorHandler } + }) + + // Force the static instance (for components that use SensorHandler.getInstance()) + val instanceField = SensorHandler::class.java.getDeclaredField("instance") + instanceField.isAccessible = true + instanceField.set(null, mockSensorHandler) + + // setupActivity triggers Application.onCreate which starts Koin + activity = Robolectric.setupActivity(AppCompatActivity::class.java) + + // Get the ProjectManager instance that Koin is using + val projectManager = KoinJavaComponent.get(ProjectManager::class.java) + + val project = Project(application, "FormulaEditorSensorListIntegrationTest", false, true) + val sprite = Sprite("testSprite") + val script = StartScript() + sprite.addScript(script) + project.getDefaultScene().addSprite(sprite) + + projectManager.setCurrentProject(project) + projectManager.setCurrentSprite(sprite) + projectManager.setCurrentlyEditedScene(project.getDefaultScene()) + } + + @After + fun tearDown() { + restoreInitialSettings() + + // Reset SensorHandler static instance + try { + val instanceField = SensorHandler::class.java.getDeclaredField("instance") + instanceField.isAccessible = true + instanceField.set(null, null) + } catch (ignored: Exception) { + // Ignore clean up errors + } + + ProjectManager.getInstance().resetProjectManager() + } + + /** + * Builds the expected list of (name, header?) pairs matching the production + * [CategoryListItems.getSensorItems] output, then asserts every item matches. + */ + @Test + fun testAllSensorListItemsCompleteAndOrdered() { + val expected = buildExpectedSensorList() + val categoryListItems = CategoryListItems() + val actual = categoryListItems.getSensorItems(activity) + val actualPairs = actual.map { it.text to it.header } + + if (expected.size != actual.size) { + println("--- SENSOR LIST MISMATCH DIFF ---") + println("Expected size: ${expected.size}, Actual size: ${actual.size}") + + val expectedNames = expected.map { it.first }.toSet() + val actualNames = actualPairs.map { it.first }.toSet() + + println("Missing from Actual: ${expectedNames - actualNames}") + println("Extra in Actual: ${actualNames - expectedNames}") + println("---------------------------------") + } + + assertEquals( + "Sensor list size mismatch. Missing: ${expected.size - actual.size} items.", + expected, actualPairs + ) + + expected.forEachIndexed { index, (expectedName, expectedHeader) -> + val item = actual[index] + assertEquals( + "Sensor name mismatch at index $index (expected: $expectedName)", + expectedName, item.text + ) + if (expectedHeader != null) { + assertNotNull( + "Expected header '$expectedHeader' at index $index but was null", + item.header + ) + assertEquals( + "Header mismatch at index $index", + expectedHeader, item.header + ) + } else { + assertNull( + "Expected no header at index $index but found '${item.header}'", + item.header + ) + } + } + } + + @Test + fun testSensorListIsNotEmpty() { + val categoryListItems = CategoryListItems() + val sensorItems = categoryListItems.getSensorItems(activity) + assert(sensorItems.isNotEmpty()) { "Sensor list should not be empty" } + } + + @Test + fun testFirstItemInEachSectionHasHeader() { + val categoryListItems = CategoryListItems() + val sensorItems = categoryListItems.getSensorItems(activity) + + val expectedHeaders = listOf( + str(R.string.formula_editor_device_lego_nxt), + str(R.string.formula_editor_device_lego_ev3), + str(R.string.formula_editor_device_phiro), + str(R.string.formula_editor_device_arduino), + str(R.string.formula_editor_device_drone), + str(R.string.formula_editor_device_raspberry), + str(R.string.formula_editor_device_nfc), + str(R.string.formula_editor_device_cast), + str(R.string.formula_editor_speech_recognition), + str(R.string.formula_editor_device_face_detection), + str(R.string.formula_editor_device_pose_detection), + str(R.string.formula_editor_device_text_recognition), + str(R.string.formula_editor_device_object_recognition), + str(R.string.formula_editor_device_sensors), + str(R.string.formula_editor_device_touch_detection), + str(R.string.formula_editor_device_date_and_time) + ) + + val actualHeaders = sensorItems.mapNotNull { it.header } + assertEquals( + "Number of section headers mismatch", + expectedHeaders.size, actualHeaders.size + ) + expectedHeaders.forEachIndexed { i, expected -> + assertEquals("Header $i mismatch", expected, actualHeaders[i]) + } + } + + // Build expected sensor list - used for verification against CategoryListItems.getSensorItems + private fun buildExpectedSensorList(): List> { + val items = mutableListOf>() + + // NXT + items.addAll(buildNxtExpected()) + // EV3 + items.addAll(buildEv3Expected()) + // Phiro + items.addAll(buildPhiroExpected()) + // Arduino + items.addAll(buildArduinoExpected()) + // Drone + items.addAll(buildDroneExpected()) + // Raspberry + items.addAll(buildRaspberryExpected()) + // NFC + items.addAll(buildNfcExpected()) + // Cast + items.addAll(buildCastExpected()) + // Speech Recognition + items.addAll(buildSpeechExpected()) + // Face Detection + items.addAll(buildFaceDetectionExpected()) + // Pose Detection + items.addAll(buildPoseDetectionExpected()) + // Text Recognition + items.addAll(buildTextRecognitionExpected()) + // Object Detection + items.addAll(buildObjectDetectionExpected()) + // Device Sensors + items.addAll(buildDeviceSensorsExpected()) + // Touch Detection + items.addAll(buildTouchDetectionExpected()) + // Date/Time + items.addAll(buildDateTimeExpected()) + + return items + } + + private fun buildNxtExpected(): List> { + val h = str(R.string.formula_editor_device_lego_nxt) + return listOf( + str(R.string.formula_editor_sensor_lego_nxt_touch) to h, + str(R.string.formula_editor_sensor_lego_nxt_sound) to null, + str(R.string.formula_editor_sensor_lego_nxt_light) to null, + str(R.string.formula_editor_sensor_lego_nxt_light_active) to null, + str(R.string.formula_editor_sensor_lego_nxt_ultrasonic) to null + ) + } + + private fun buildEv3Expected(): List> { + val h = str(R.string.formula_editor_device_lego_ev3) + return listOf( + str(R.string.formula_editor_sensor_lego_ev3_sensor_touch) to h, + str(R.string.formula_editor_sensor_lego_ev3_sensor_infrared) to null, + str(R.string.formula_editor_sensor_lego_ev3_sensor_color) to null, + str(R.string.formula_editor_sensor_lego_ev3_sensor_color_ambient) to null, + str(R.string.formula_editor_sensor_lego_ev3_sensor_color_reflected) to null, + str(R.string.formula_editor_sensor_lego_ev3_sensor_hitechnic_color) to null, + str(R.string.formula_editor_sensor_lego_ev3_sensor_nxt_temperature_c) to null, + str(R.string.formula_editor_sensor_lego_ev3_sensor_nxt_temperature_f) to null, + str(R.string.formula_editor_sensor_lego_ev3_sensor_nxt_light) to null, + str(R.string.formula_editor_sensor_lego_ev3_sensor_nxt_light_active) to null, + str(R.string.formula_editor_sensor_lego_ev3_sensor_nxt_sound) to null, + str(R.string.formula_editor_sensor_lego_ev3_sensor_nxt_ultrasonic) to null + ) + } + + private fun buildPhiroExpected(): List> { + val h = str(R.string.formula_editor_device_phiro) + return listOf( + str(R.string.formula_editor_phiro_sensor_front_left) to h, + str(R.string.formula_editor_phiro_sensor_front_right) to null, + str(R.string.formula_editor_phiro_sensor_side_left) to null, + str(R.string.formula_editor_phiro_sensor_side_right) to null, + str(R.string.formula_editor_phiro_sensor_bottom_left) to null, + str(R.string.formula_editor_phiro_sensor_bottom_right) to null + ) + } + + private fun buildArduinoExpected(): List> { + val h = str(R.string.formula_editor_device_arduino) + val p = str(R.string.formula_editor_function_pin_default_parameter) + return listOf( + (str(R.string.formula_editor_function_arduino_read_pin_value_analog) + p) to h, + (str(R.string.formula_editor_function_arduino_read_pin_value_digital) + p) to null + ) + } + + private fun buildDroneExpected(): List> { + val h = str(R.string.formula_editor_device_drone) + return listOf( + str(R.string.formula_editor_sensor_drone_battery_status) to h, + str(R.string.formula_editor_sensor_drone_emergency_state) to null, + str(R.string.formula_editor_sensor_drone_flying) to null, + str(R.string.formula_editor_sensor_drone_initialized) to null, + str(R.string.formula_editor_sensor_drone_usb_active) to null, + str(R.string.formula_editor_sensor_drone_usb_remaining_time) to null, + str(R.string.formula_editor_sensor_drone_camera_ready) to null, + str(R.string.formula_editor_sensor_drone_record_ready) to null, + str(R.string.formula_editor_sensor_drone_recording) to null, + str(R.string.formula_editor_sensor_drone_num_frames) to null + ) + } + + private fun buildRaspberryExpected(): List> { + val h = str(R.string.formula_editor_device_raspberry) + val p = str(R.string.formula_editor_function_pin_default_parameter) + return listOf( + (str(R.string.formula_editor_function_raspi_read_pin_value_digital) + p) to h + ) + } + + private fun buildNfcExpected(): List> { + val h = str(R.string.formula_editor_device_nfc) + return listOf( + str(R.string.formula_editor_nfc_tag_id) to h, + str(R.string.formula_editor_nfc_tag_message) to null + ) + } + + private fun buildCastExpected(): List> { + val h = str(R.string.formula_editor_device_cast) + return listOf( + str(R.string.formula_editor_sensor_gamepad_a_pressed) to h, + str(R.string.formula_editor_sensor_gamepad_b_pressed) to null, + str(R.string.formula_editor_sensor_gamepad_up_pressed) to null, + str(R.string.formula_editor_sensor_gamepad_down_pressed) to null, + str(R.string.formula_editor_sensor_gamepad_left_pressed) to null, + str(R.string.formula_editor_sensor_gamepad_right_pressed) to null + ) + } + + private fun buildSpeechExpected(): List> { + val h = str(R.string.formula_editor_speech_recognition) + return listOf( + str(R.string.formula_editor_listening_language_sensor) to h + ) + } + + private fun buildFaceDetectionExpected(): List> { + val h = str(R.string.formula_editor_device_face_detection) + val np = str(R.string.formula_editor_function_no_parameter) + return listOf( + (str(R.string.formula_editor_sensor_face_detected) + np) to h, + (str(R.string.formula_editor_sensor_face_size) + np) to null, + (str(R.string.formula_editor_sensor_face_x_position) + np) to null, + (str(R.string.formula_editor_sensor_face_y_position) + np) to null, + (str(R.string.formula_editor_sensor_second_face_detected) + np) to null, + (str(R.string.formula_editor_sensor_second_face_size) + np) to null, + (str(R.string.formula_editor_sensor_second_face_x_position) + np) to null, + (str(R.string.formula_editor_sensor_second_face_y_position) + np) to null + ) + } + + private fun buildPoseDetectionExpected(): List> { + val h = str(R.string.formula_editor_device_pose_detection) + val np = str(R.string.formula_editor_function_no_parameter) + val sensorResIds = listOf( + R.string.formula_editor_sensor_nose_x, + R.string.formula_editor_sensor_nose_y, + R.string.formula_editor_sensor_left_eye_inner_x, + R.string.formula_editor_sensor_left_eye_inner_y, + R.string.formula_editor_sensor_left_eye_center_x, + R.string.formula_editor_sensor_left_eye_center_y, + R.string.formula_editor_sensor_left_eye_outer_x, + R.string.formula_editor_sensor_left_eye_outer_y, + R.string.formula_editor_sensor_right_eye_inner_x, + R.string.formula_editor_sensor_right_eye_inner_y, + R.string.formula_editor_sensor_right_eye_center_x, + R.string.formula_editor_sensor_right_eye_center_y, + R.string.formula_editor_sensor_right_eye_outer_x, + R.string.formula_editor_sensor_right_eye_outer_y, + R.string.formula_editor_sensor_left_ear_x, + R.string.formula_editor_sensor_left_ear_y, + R.string.formula_editor_sensor_right_ear_x, + R.string.formula_editor_sensor_right_ear_y, + R.string.formula_editor_sensor_mouth_left_corner_x, + R.string.formula_editor_sensor_mouth_left_corner_y, + R.string.formula_editor_sensor_mouth_right_corner_x, + R.string.formula_editor_sensor_mouth_right_corner_y, + R.string.formula_editor_sensor_left_shoulder_x, + R.string.formula_editor_sensor_left_shoulder_y, + R.string.formula_editor_sensor_right_shoulder_x, + R.string.formula_editor_sensor_right_shoulder_y, + R.string.formula_editor_sensor_left_elbow_x, + R.string.formula_editor_sensor_left_elbow_y, + R.string.formula_editor_sensor_right_elbow_x, + R.string.formula_editor_sensor_right_elbow_y, + R.string.formula_editor_sensor_left_wrist_x, + R.string.formula_editor_sensor_left_wrist_y, + R.string.formula_editor_sensor_right_wrist_x, + R.string.formula_editor_sensor_right_wrist_y, + R.string.formula_editor_sensor_left_pinky_knuckle_x, + R.string.formula_editor_sensor_left_pinky_knuckle_y, + R.string.formula_editor_sensor_right_pinky_knuckle_x, + R.string.formula_editor_sensor_right_pinky_knuckle_y, + R.string.formula_editor_sensor_left_index_knuckle_x, + R.string.formula_editor_sensor_left_index_knuckle_y, + R.string.formula_editor_sensor_right_index_knuckle_x, + R.string.formula_editor_sensor_right_index_knuckle_y, + R.string.formula_editor_sensor_left_thumb_knuckle_x, + R.string.formula_editor_sensor_left_thumb_knuckle_y, + R.string.formula_editor_sensor_right_thumb_knuckle_x, + R.string.formula_editor_sensor_right_thumb_knuckle_y, + R.string.formula_editor_sensor_left_hip_x, + R.string.formula_editor_sensor_left_hip_y, + R.string.formula_editor_sensor_right_hip_x, + R.string.formula_editor_sensor_right_hip_y, + R.string.formula_editor_sensor_left_knee_x, + R.string.formula_editor_sensor_left_knee_y, + R.string.formula_editor_sensor_right_knee_x, + R.string.formula_editor_sensor_right_knee_y, + R.string.formula_editor_sensor_left_ankle_x, + R.string.formula_editor_sensor_left_ankle_y, + R.string.formula_editor_sensor_right_ankle_x, + R.string.formula_editor_sensor_right_ankle_y, + R.string.formula_editor_sensor_left_heel_x, + R.string.formula_editor_sensor_left_heel_y, + R.string.formula_editor_sensor_right_heel_x, + R.string.formula_editor_sensor_right_heel_y, + R.string.formula_editor_sensor_left_foot_index_x, + R.string.formula_editor_sensor_left_foot_index_y, + R.string.formula_editor_sensor_right_foot_index_x, + R.string.formula_editor_sensor_right_foot_index_y + ) + return sensorResIds.mapIndexed { i, resId -> + (str(resId) + np) to (if (i == 0) h else null) + } + } + + private fun buildTextRecognitionExpected(): List> { + val h = str(R.string.formula_editor_device_text_recognition) + val np = str(R.string.formula_editor_function_no_parameter) + val bp = str(R.string.formula_editor_function_text_block_parameter) + return listOf( + (str(R.string.formula_editor_sensor_text_from_camera) + np) to h, + (str(R.string.formula_editor_sensor_text_blocks_number) + np) to null, + (str(R.string.formula_editor_function_text_block_x) + bp) to null, + (str(R.string.formula_editor_function_text_block_y) + bp) to null, + (str(R.string.formula_editor_function_text_block_size) + bp) to null, + (str(R.string.formula_editor_function_text_block_from_camera) + bp) to null, + (str(R.string.formula_editor_function_text_block_language_from_camera) + bp) to null + ) + } + + private fun buildObjectDetectionExpected(): List> { + val h = str(R.string.formula_editor_device_object_recognition) + return listOf( + str(R.string.formula_editor_function_get_id_of_detected_object) to h, + str(R.string.formula_editor_function_object_with_id_visible) to null + ) + } + + private fun buildDeviceSensorsExpected(): List> { + val h = str(R.string.formula_editor_device_sensors) + val items = mutableListOf>() + items.add(str(R.string.formula_editor_sensor_loudness) to h) + items.add(str(R.string.formula_editor_function_touched) to null) + items.add(str(R.string.formula_editor_sensor_stage_width) to null) + items.add(str(R.string.formula_editor_sensor_stage_height) to null) + items.add((str(R.string.formula_editor_sensor_color_at_x_y) + + str(R.string.formula_editor_sensor_color_at_x_y_parameter)) to null) + items.add((str(R.string.formula_editor_sensor_color_equals_color) + + str(R.string.formula_editor_sensor_color_equals_color_parameter)) to null) + items.add(str(R.string.formula_editor_sensor_x_acceleration) to null) + items.add(str(R.string.formula_editor_sensor_y_acceleration) to null) + items.add(str(R.string.formula_editor_sensor_z_acceleration) to null) + items.add(str(R.string.formula_editor_sensor_x_inclination) to null) + items.add(str(R.string.formula_editor_sensor_y_inclination) to null) + items.add(str(R.string.formula_editor_sensor_compass_direction) to null) + items.add(str(R.string.formula_editor_sensor_latitude) to null) + items.add(str(R.string.formula_editor_sensor_longitude) to null) + items.add(str(R.string.formula_editor_sensor_location_accuracy) to null) + items.add(str(R.string.formula_editor_sensor_altitude) to null) + items.add(str(R.string.formula_editor_sensor_user_language) to null) + return items + } + + private fun buildTouchDetectionExpected(): List> { + val h = str(R.string.formula_editor_device_touch_detection) + val np = str(R.string.formula_editor_function_no_parameter) + val tp = str(R.string.formula_editor_function_touch_parameter) + return listOf( + (str(R.string.formula_editor_function_finger_x) + np) to h, + (str(R.string.formula_editor_function_finger_y) + np) to null, + (str(R.string.formula_editor_function_is_finger_touching) + np) to null, + (str(R.string.formula_editor_function_multi_finger_x) + tp) to null, + (str(R.string.formula_editor_function_multi_finger_y) + tp) to null, + (str(R.string.formula_editor_function_is_multi_finger_touching) + tp) to null, + (str(R.string.formula_editor_function_index_of_last_finger) + np) to null, + (str(R.string.formula_editor_function_number_of_current_touches) + np) to null, + (str(R.string.formula_editor_function_index_of_current_touch) + tp) to null + ) + } + + private fun buildDateTimeExpected(): List> { + val h = str(R.string.formula_editor_device_date_and_time) + return listOf( + str(R.string.formula_editor_sensor_timer) to h, + str(R.string.formula_editor_sensor_date_year) to null, + str(R.string.formula_editor_sensor_date_month) to null, + str(R.string.formula_editor_sensor_date_day) to null, + str(R.string.formula_editor_sensor_date_weekday) to null, + str(R.string.formula_editor_sensor_time_hour) to null, + str(R.string.formula_editor_sensor_time_minute) to null, + str(R.string.formula_editor_sensor_time_second) to null + ) + } + + // Settings helpers + + private fun saveInitialSettings() { + val prefs = PreferenceManager + .getDefaultSharedPreferences(RuntimeEnvironment.getApplication()) + allShowBrickSettings.forEach { setting -> + initialSettings[setting] = prefs.getBoolean(setting, false) + } + } + + private fun restoreInitialSettings() { + val editor = PreferenceManager + .getDefaultSharedPreferences(RuntimeEnvironment.getApplication()).edit() + allShowBrickSettings.forEach { setting -> + editor.putBoolean(setting, initialSettings[setting] ?: false) + } + editor.commit() + } + + private fun setAllShowBrickSettingsToTrue() { + val editor = PreferenceManager + .getDefaultSharedPreferences(RuntimeEnvironment.getApplication()).edit() + allShowBrickSettings.forEach { setting -> + editor.putBoolean(setting, true) + } + editor.commit() + } +}