// Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtPositioning import QtQml import QtQuick import QtTest TestCase { name: "SatelliteSource" id: top Component { id: defaultSource SatelliteSource { } } Component { id: sourceWithParameters SatelliteSource { name: "satellitesource.test" PluginParameter { name: "satellitesystem"; value: GeoSatelliteInfo.GALILEO } } } Component { id: sourceWithIncompleteParameter SatelliteSource { name: "satellitesource.test" PluginParameter { objectName: "incompleteProperty" name: "satellitesystem" } } } SignalSpy { id: inViewSpy; signalName: "satellitesInViewChanged" } SignalSpy { id: inUseSpy; signalName: "satellitesInUseChanged" } SignalSpy { id: activeSpy; signalName: "activeChanged" } SignalSpy { id: validSpy; signalName: "validityChanged" } SignalSpy { id: updateIntervalSpy; signalName: "updateIntervalChanged" } SignalSpy { id: errorSpy; signalName: "sourceErrorChanged" } SignalSpy { id: nameSpy; signalName: "nameChanged" } function test_default_construct() { var source = defaultSource.createObject(top, {}) // empty name resulted in picking a default source, which is valid compare(source.valid, true) compare(source.active, false) verify(source.name !== "") compare(source.sourceError, SatelliteSource.NoError) compare(source.satellitesInView.length, 0) compare(source.satellitesInUse.length, 0) } function test_unknown_plugin() { var source = defaultSource.createObject(top, { name: "invalid_plugin" }) compare(source.valid, false) compare(source.active, false) compare(source.name, "invalid_plugin") compare(source.sourceError, SatelliteSource.NoError) compare(source.satellitesInView.length, 0) compare(source.satellitesInUse.length, 0) } function test_backend_properties() { var source = defaultSource.createObject(top, { name: "satellitesource.test" }) var inView = source.backendProperty("desiredInViewCount") var inUse = source.backendProperty("desiredInUseCount") var useElevation = source.backendProperty("useElevation") var useAzimuth = source.backendProperty("useAzimuth") var unknownParam = source.backendProperty("unknown") // check defaults compare(inView, 5) compare(inUse, 3) compare(useElevation, false) compare(useAzimuth, false) compare(unknownParam, undefined) // modify compare(source.setBackendProperty("desiredInViewCount", 10), true) compare(source.setBackendProperty("desiredInUseCount", 5), true) compare(source.setBackendProperty("useElevation", true), true) compare(source.setBackendProperty("useAzimuth", true), true) compare(source.setBackendProperty("unknown", "val"), false) // check that parameters are updated inView = source.backendProperty("desiredInViewCount") inUse = source.backendProperty("desiredInUseCount") useElevation = source.backendProperty("useElevation") useAzimuth = source.backendProperty("useAzimuth") compare(inView, 10) compare(inUse, 5) compare(useElevation, true) compare(useAzimuth, true) } function test_regular_updates() { var source = defaultSource.createObject(top, { name: "satellitesource.test" }) inViewSpy.target = source inUseSpy.target = source inViewSpy.clear() inUseSpy.clear() var expectedInView = source.backendProperty("desiredInViewCount") var expectedInUse = source.backendProperty("desiredInUseCount") verify(expectedInView > 0) verify(expectedInUse > 0) source.start() tryCompare(inViewSpy, "count", 1) tryCompare(inUseSpy, "count", 1) compare(source.satellitesInView.length, expectedInView) compare(source.satellitesInUse.length, expectedInUse) for (var idx = 0; idx < expectedInView; ++idx) { var satInView = source.satellitesInView[idx] compare(satInView.satelliteSystem, GeoSatelliteInfo.GPS) compare(satInView.satelliteIdentifier, idx + 1) compare(satInView.signalStrength, 5 * (idx + 1)) compare(satInView.hasAttribute(GeoSatelliteInfo.Elevation), false) compare(satInView.attribute(GeoSatelliteInfo.Elevation), -1) compare(satInView.hasAttribute(GeoSatelliteInfo.Azimuth), false) compare(satInView.attribute(GeoSatelliteInfo.Azimuth), -1) if (idx < expectedInUse) { var satInUse = source.satellitesInUse[idx] compare(satInUse, satInView) } } } function test_update_with_plugin_parameters_and_properties() { var source = sourceWithParameters.createObject(top, {}) inViewSpy.target = source inUseSpy.target = source inViewSpy.clear() inUseSpy.clear() source.setBackendProperty("useElevation", true) source.setBackendProperty("useAzimuth", true) var expectedInView = source.backendProperty("desiredInViewCount") var expectedInUse = source.backendProperty("desiredInUseCount") source.update() tryCompare(inViewSpy, "count", 1) tryCompare(inUseSpy, "count", 1) for (var idx = 0; idx < expectedInView; ++idx) { var satInView = source.satellitesInView[idx] compare(satInView.satelliteSystem, GeoSatelliteInfo.GALILEO) compare(satInView.satelliteIdentifier, idx + 1) compare(satInView.signalStrength, 5 * (idx + 1)) compare(satInView.hasAttribute(GeoSatelliteInfo.Elevation), true) compare(satInView.attribute(GeoSatelliteInfo.Elevation), 3.0 * (idx + 1)) compare(satInView.hasAttribute(GeoSatelliteInfo.Azimuth), true) compare(satInView.attribute(GeoSatelliteInfo.Azimuth), 0.5 * (idx + 1)) if (idx < expectedInUse) { var satInUse = source.satellitesInUse[idx] compare(satInUse, satInView) } } } function test_signals() { var source = defaultSource.createObject(top, { name: "satellitesource.test" }) activeSpy.target = source activeSpy.clear() validSpy.target = source validSpy.clear() updateIntervalSpy.target = source updateIntervalSpy.clear() nameSpy.target = source nameSpy.clear() errorSpy.target = source errorSpy.clear() compare(source.valid, true) source.updateInterval = 1000 tryCompare(updateIntervalSpy, "count", 1) compare(source.updateInterval, 1000) source.active = true tryCompare(activeSpy, "count", 1) compare(source.active, true) // should trigger an error, because it's less than minimumUpdateInterval source.update(100) tryCompare(errorSpy, "count", 1) compare(source.sourceError, SatelliteSource.UpdateTimeoutError) source.name = "invalid_plugin" tryCompare(nameSpy, "count", 1) compare(source.name, "invalid_plugin") tryCompare(activeSpy, "count", 2) compare(source.active, false) tryCompare(validSpy, "count", 1) compare(source.valid, false) } function test_create_active() { // create a default source, active by default - should be working fine var source = defaultSource.createObject(top, { active: true }) inViewSpy.target = source inViewSpy.clear() inUseSpy.target = source inUseSpy.clear() tryCompare(source, "active", true) tryCompare(source, "valid", true) tryVerify(function() { return inViewSpy.count > 0 && inUseSpy.count > 0 }) verify(source.satellitesInView.length > 0) verify(source.satellitesInUse.length > 0) // create an invalid source, active by default - should fail var invalidSource = defaultSource.createObject(top, { name: "invalid", active: true }) tryCompare(invalidSource, "active", false) tryCompare(invalidSource, "valid", false) } function test_set_plugin() { // When creating with no name specified, some default plugin will // be chosen var source = defaultSource.createObject(top, {}) nameSpy.target = source nameSpy.clear() activeSpy.target = source activeSpy.clear() compare(source.valid, true) var defaultPluginName = source.name source.active = true tryCompare(activeSpy, "count", 1) // check that setting an empty name will do nothing, as we are already // using the default source.name = "" compare(nameSpy.count, 0) compare(source.name, defaultPluginName) compare(source.active, true) // Set another valid plugin name. Source will be set to inactive state, // but will be valid var otherPluginName = (defaultPluginName === "satellitesource.test") ? "dummy.source" : "satellitesource.test"; source.name = otherPluginName compare(nameSpy.count, 1) compare(source.name, otherPluginName) tryCompare(activeSpy, "count", 2) compare(source.active, false) compare(source.valid, true) source.active = true tryCompare(activeSpy, "count", 3) compare(source.active, true) // Set invalid name. Source will be set to inactive state, and will be // invalid source.name = "invalid_name" compare(nameSpy.count, 2) compare(source.name, "invalid_name") tryCompare(activeSpy, "count", 4) compare(source.active, false) compare(source.valid, false) } function test_update_after_start() { // When update() is called after start(), it should not invalidate any // state. The active state must still be true when the single update() // is completed. var source = defaultSource.createObject(top, { name: "satellitesource.test" }) errorSpy.target = source errorSpy.clear() inViewSpy.target = source inViewSpy.clear() compare(source.active, false) source.start() tryCompare(source, "active", true) source.update(300) // default update interval == 200 tryVerify(function() { return errorSpy.count > 0 || inViewSpy.count > 0 }) // at this point update() is completed, source should still be active compare(source.active, true) source.stop() tryCompare(source, "active", false) } function test_start_after_update() { // When start() is called after update(), the source should remain // active even when the single update is completed. var source = defaultSource.createObject(top, { name: "satellitesource.test" }) errorSpy.target = source errorSpy.clear() inViewSpy.target = source inViewSpy.clear() compare(source.active, false) source.update(300) tryCompare(source, "active", true) source.start() tryVerify(function() { return errorSpy.count > 0 || inViewSpy.count > 0 }) // at this point update() is completed, source should still be active compare(source.active, true) source.stop() tryCompare(source, "active", false) } function test_stop_after_update() { // When stop() is called after update(), and the update() is still in // progress, the source should remain active until the update() // is completed. var source = defaultSource.createObject(top, { name: "satellitesource.test" }) errorSpy.target = source errorSpy.clear() inViewSpy.target = source inViewSpy.clear() compare(source.active, false) source.start() tryCompare(source, "active", true) source.update(300) // default update interval == 200 source.stop(); compare(source.active, true) tryVerify(function() { return errorSpy.count > 0 || inViewSpy.count > 0 }) // at this point update() is completed, source should switch to inactive compare(source.active, false) } function test_start_stop_after_update() { // Calling start() and stop() after update(), while still waiting for // the update() to complete, should still result in the source to be // active until the update() is completed. var source = defaultSource.createObject(top, { name: "satellitesource.test" }) errorSpy.target = source errorSpy.clear() inViewSpy.target = source inViewSpy.clear() compare(source.active, false) source.update(300) tryCompare(source, "active", true) source.start() source.stop() compare(source.active, true) tryVerify(function() { return errorSpy.count > 0 || inViewSpy.count > 0 }) // at this point update() is completed, source should switch to inactive compare(source.active, false) } function test_update_timed_out() { // This test checks that source resets to inactive state when the single // update() request times out without providing the position info var source = defaultSource.createObject(top, { name: "satellitesource.test" }) errorSpy.target = source errorSpy.clear() compare(source.active, false) source.update(50) // too small interval compare(source.active, true) tryCompare(errorSpy, "count", 1) compare(source.active, false) } function test_update_with_start_timed_out() { // This test checks that if single update() times out, but the regular // updates are running, we still remain in active state. var source = defaultSource.createObject(top, { name: "satellitesource.test" }) errorSpy.target = source errorSpy.clear() compare(source.active, false) source.start() source.update(100) // too small interval tryCompare(source, "active", true) tryCompare(errorSpy, "count", 1) compare(source.active, true) // still active source.stop() tryCompare(source, "active", false) } function test_start_update_stop_with_no_intervals() { // This test checks that calls start() -> update() -> stop() without // any waits between them will result in expected behavior. // Specifically, the source should remain active until it gets // the satellite updates. var source = defaultSource.createObject(top, { name: "satellitesource.test" }) inViewSpy.target = source inViewSpy.clear() compare(source.active, false) source.start() source.update(300) source.stop() tryCompare(source, "active", true) tryCompare(inViewSpy, "count", 1) // after the update is received, the source switches to inactive state compare(source.active, false) } function test_start_when_incomplete() { var source = sourceWithIncompleteParameter.createObject(top, {}) inViewSpy.target = source inViewSpy.clear() var parameterObj = findChild(source, "incompleteProperty") source.start() // does nothing, as the parameter is not initialized compare(source.active, false) // init parameter parameterObj.value = GeoSatelliteInfo.BEIDOU // source created - should start providing updates tryCompare(source, "active", true) tryVerify(function() { return inViewSpy.count > 0 }) source.stop() tryCompare(source, "active", false) } function test_start_stop_when_incomplete() { var source = sourceWithIncompleteParameter.createObject(top, {}) var parameterObj = findChild(source, "incompleteProperty") source.start() // does nothing, as the parameter is not initialized compare(source.active, false) source.stop() // calling stop() should invalidate request for start // init parameter parameterObj.value = GeoSatelliteInfo.BEIDOU // source still should be inactive tryCompare(source, "active", false) } function test_update_when_incomplete() { var source = sourceWithIncompleteParameter.createObject(top, {}) inViewSpy.target = source inViewSpy.clear() var parameterObj = findChild(source, "incompleteProperty") source.update() // does nothing, as the parameter is not initialized compare(source.active, false) // init parameter parameterObj.value = GeoSatelliteInfo.BEIDOU // source created - should provide exactly 1 update tryCompare(source, "active", true) tryVerify(function() { return inViewSpy.count == 1 }) tryCompare(source, "active", false) } }