package com.rusefi; import com.devexperts.logging.Logging; import com.opensr5.ConfigurationImage; import com.opensr5.ini.field.ScalarIniField; import com.rusefi.binaryprotocol.BinaryProtocol; import com.rusefi.config.generated.Fields; import com.rusefi.core.rusEFIVersion; import com.rusefi.io.ConnectionStateListener; import com.rusefi.io.LinkManager; import com.rusefi.io.tcp.BinaryProtocolServer; import com.rusefi.io.tcp.TcpConnector; import com.rusefi.io.tcp.TcpIoStream; import com.rusefi.proxy.NetworkConnector; import com.rusefi.proxy.NetworkConnectorContext; import com.rusefi.proxy.client.LocalApplicationProxy; import com.rusefi.proxy.client.LocalApplicationProxyContext; import com.rusefi.proxy.client.UpdateType; import com.rusefi.server.*; import com.rusefi.tools.online.HttpUtil; import org.apache.http.HttpResponse; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.net.MalformedURLException; import java.util.Objects; import java.util.concurrent.CountDownLatch; import static com.devexperts.logging.Logging.getLogging; import static com.rusefi.TestHelper.*; import static com.rusefi.Timeouts.SECOND; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; public class FullServerTest { private static final Logging log = getLogging(FullServerTest.class); @Before public void setup() throws MalformedURLException { BackendTestHelper.commonServerTest(); } @Test public void testRelayWorkflow() throws InterruptedException, IOException { ScalarIniField iniField = TestHelper.createIniField(Fields.CYLINDERSCOUNT); int value = 241; int userId = 7; LocalApplicationProxyContext localApplicationProxyContext = new LocalApplicationProxyContext() { @Override public String executeGet(String url) throws IOException { return HttpUtil.executeGet(url); } @Override public int serverPortForRemoteApplications() { return 7003; } @Override public int authenticatorPort() { return 7004; } }; CountDownLatch controllerRegistered = new CountDownLatch(1); CountDownLatch applicationClosed = new CountDownLatch(1); UserDetailsResolver userDetailsResolver = authToken -> new UserDetails(authToken.substring(0, 5), userId); int httpPort = 8103; int applicationTimeout = 7 * SECOND; try (Backend backend = new Backend(userDetailsResolver, httpPort, applicationTimeout) { @Override public void register(ControllerConnectionState controllerConnectionState) { super.register(controllerConnectionState); controllerRegistered.countDown(); } @Override protected void close(ApplicationConnectionState applicationConnectionState) { super.close(applicationConnectionState); applicationClosed.countDown(); } }; LinkManager clientManager = new LinkManager().setNeedPullData(false); NetworkConnector networkConnector = new NetworkConnector()) { int serverPortForControllers = 7001; // first start backend server BackendTestHelper.runControllerConnectorBlocking(backend, serverPortForControllers); BackendTestHelper.runApplicationConnectorBlocking(backend, localApplicationProxyContext.serverPortForRemoteApplications()); // create virtual controller to which "rusEFI network connector" connects to int controllerPort = 7002; ConfigurationImage controllerImage = prepareImage(value, createIniField(Fields.CYLINDERSCOUNT)); TestHelper.createVirtualController(controllerPort, controllerImage, new BinaryProtocolServer.Context()); CountDownLatch softwareUpdateRequest = new CountDownLatch(1); NetworkConnectorContext networkConnectorContext = new NetworkConnectorContext() { @Override public int serverPortForControllers() { return serverPortForControllers; } @Override public void onConnectorSoftwareUpdateToLatestRequest() { softwareUpdateRequest.countDown(); } }; // start "rusEFI network connector" to connect controller with backend since in real life controller has only local serial port it does not have network NetworkConnector.NetworkConnectorResult networkConnectorResult = networkConnector.start(NetworkConnector.Implementation.Unknown, TestHelper.TEST_TOKEN_1, TcpConnector.LOCALHOST + ":" + controllerPort, networkConnectorContext, NetworkConnector.ReconnectListener.VOID); ControllerInfo controllerInfo = networkConnectorResult.getControllerInfo(); TestHelper.assertLatch("controllerRegistered. todo: this test should not depend on internet connection and having real .ini on rusEFI online", controllerRegistered); SessionDetails authenticatorSessionDetails = new SessionDetails(NetworkConnector.Implementation.Unknown, controllerInfo, TEST_TOKEN_3, networkConnectorResult.getOneTimeToken(), rusEFIVersion.CONSOLE_VERSION); ApplicationRequest applicationRequest = new ApplicationRequest(authenticatorSessionDetails, userDetailsResolver.apply(TestHelper.TEST_TOKEN_1)); HttpResponse response = LocalApplicationProxy.requestSoftwareUpdate(httpPort, applicationRequest, UpdateType.CONTROLLER); log.info("requestSoftwareUpdate response: " + response.toString()); assertLatch("update requested", softwareUpdateRequest); // start authenticator LocalApplicationProxy.startAndRun(localApplicationProxyContext, applicationRequest, httpPort, TcpIoStream.DisconnectListener.VOID, LocalApplicationProxy.ConnectionListener.VOID); CountDownLatch connectionEstablishedCountDownLatch = new CountDownLatch(1); // connect to proxy and read virtual controller through it clientManager.startAndConnect(TcpConnector.LOCALHOST + ":" + localApplicationProxyContext.authenticatorPort(), new ConnectionStateListener() { @Override public void onConnectionEstablished() { connectionEstablishedCountDownLatch.countDown(); } @Override public void onConnectionFailed(String s) { System.out.println("Failed"); } }); assertLatch("Proxied ECU Connection established", connectionEstablishedCountDownLatch); BinaryProtocol clientStreamState = clientManager.getCurrentStreamState(); Objects.requireNonNull(clientStreamState, "clientStreamState"); ConfigurationImage clientImage = clientStreamState.getControllerConfiguration(); String clientValue = iniField.getValue(clientImage); assertEquals(Double.toString(value), clientValue); assertEquals(1, backend.getApplications().size()); assertEquals(1, applicationClosed.getCount()); // now let's test that application connector would be terminated by server due to inactivity log.info("**************************************"); log.info("Sleeping twice the application timeout"); log.info("**************************************"); assertLatch("applicationClosed", applicationClosed, 3 * applicationTimeout); assertEquals("applications size", 0, backend.getApplications().size()); } } }