package com.battery.device.registry;
|
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertSame;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
import java.util.ArrayList;
|
import java.util.List;
|
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.Executors;
|
import java.util.concurrent.Future;
|
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
import org.junit.jupiter.api.Test;
|
import com.battery.device.api.DeviceAdapter;
|
import com.battery.device.model.AdapterCapabilities;
|
import com.battery.device.model.ChannelIdentity;
|
import com.battery.device.model.DeviceControlCommand;
|
|
class DeviceAdapterRegistryTest
|
{
|
@Test
|
void synchronizesEnabledConfigurationWithoutRecreatingUnchangedAdapter()
|
{
|
RecordingFactory factory = new RecordingFactory();
|
try (DeviceAdapterRegistry registry = new DeviceAdapterRegistry(List.of(factory)))
|
{
|
AdapterInstanceConfig config = config("127.0.0.1", true);
|
|
registry.synchronize(config);
|
DeviceAdapter first = registry.execute("lab-bts", adapter -> adapter);
|
registry.synchronize(config);
|
|
assertSame(first, registry.execute("lab-bts", adapter -> adapter));
|
assertFalse(factory.adapters.get(0).closed.get());
|
}
|
}
|
|
@Test
|
void replacesChangedConfigurationAndClosesAdapterWhenDisabled()
|
{
|
RecordingFactory factory = new RecordingFactory();
|
try (DeviceAdapterRegistry registry = new DeviceAdapterRegistry(List.of(factory)))
|
{
|
registry.synchronize(config("127.0.0.1", true));
|
StubAdapter first = factory.adapters.get(0);
|
|
registry.synchronize(config("127.0.0.2", true));
|
|
assertTrue(first.closed.get());
|
assertNotSame(first, registry.execute("lab-bts", adapter -> adapter));
|
|
registry.synchronize(config("127.0.0.2", false));
|
|
assertTrue(factory.adapters.get(1).closed.get());
|
assertFalse(registry.contains("lab-bts"));
|
assertThrows(AdapterRegistryException.class,
|
() -> registry.execute("lab-bts", adapter -> adapter));
|
}
|
}
|
|
@Test
|
void serializesOperationsForTheSameAdapterInstance() throws Exception
|
{
|
RecordingFactory factory = new RecordingFactory();
|
try (DeviceAdapterRegistry registry = new DeviceAdapterRegistry(List.of(factory)))
|
{
|
registry.synchronize(config("127.0.0.1", true));
|
CountDownLatch firstEntered = new CountDownLatch(1);
|
CountDownLatch releaseFirst = new CountDownLatch(1);
|
CountDownLatch secondEntered = new CountDownLatch(1);
|
ExecutorService executor = Executors.newFixedThreadPool(2);
|
try
|
{
|
Future<?> first = executor.submit(() -> registry.execute("lab-bts", adapter -> {
|
firstEntered.countDown();
|
await(releaseFirst);
|
return null;
|
}));
|
assertTrue(firstEntered.await(1, TimeUnit.SECONDS));
|
|
Future<?> second = executor.submit(() -> registry.execute("lab-bts", adapter -> {
|
secondEntered.countDown();
|
return null;
|
}));
|
|
assertFalse(secondEntered.await(100, TimeUnit.MILLISECONDS));
|
releaseFirst.countDown();
|
first.get(1, TimeUnit.SECONDS);
|
second.get(1, TimeUnit.SECONDS);
|
assertTrue(secondEntered.await(1, TimeUnit.SECONDS));
|
}
|
finally
|
{
|
executor.shutdownNow();
|
}
|
}
|
}
|
|
@Test
|
void configurationReplacementWaitsForRunningOperationBeforeClosingAdapter() throws Exception
|
{
|
RecordingFactory factory = new RecordingFactory();
|
try (DeviceAdapterRegistry registry = new DeviceAdapterRegistry(List.of(factory)))
|
{
|
registry.synchronize(config("127.0.0.1", true));
|
StubAdapter firstAdapter = factory.adapters.get(0);
|
CountDownLatch operationEntered = new CountDownLatch(1);
|
CountDownLatch releaseOperation = new CountDownLatch(1);
|
CountDownLatch replacementStarted = new CountDownLatch(1);
|
ExecutorService executor = Executors.newFixedThreadPool(2);
|
try
|
{
|
Future<?> operation = executor.submit(() -> registry.execute("lab-bts", adapter -> {
|
operationEntered.countDown();
|
await(releaseOperation);
|
return null;
|
}));
|
assertTrue(operationEntered.await(1, TimeUnit.SECONDS));
|
|
Future<?> replacement = executor.submit(() -> {
|
replacementStarted.countDown();
|
registry.synchronize(config("127.0.0.2", true));
|
});
|
assertTrue(replacementStarted.await(1, TimeUnit.SECONDS));
|
assertFalse(replacement.isDone());
|
assertFalse(firstAdapter.closed.get());
|
|
releaseOperation.countDown();
|
operation.get(1, TimeUnit.SECONDS);
|
replacement.get(1, TimeUnit.SECONDS);
|
assertTrue(firstAdapter.closed.get());
|
}
|
finally
|
{
|
executor.shutdownNow();
|
}
|
}
|
}
|
|
private static void await(CountDownLatch latch)
|
{
|
try
|
{
|
if (!latch.await(1, TimeUnit.SECONDS)) throw new AssertionError("等待适配器测试信号超时");
|
}
|
catch (InterruptedException exception)
|
{
|
Thread.currentThread().interrupt();
|
throw new AssertionError("等待适配器测试信号被中断", exception);
|
}
|
}
|
|
private static AdapterInstanceConfig config(String host, boolean enabled)
|
{
|
return new AdapterInstanceConfig("lab-bts", "BTS", host, 502, 5000,
|
"Asia/Hong_Kong", enabled, AdapterCapabilities.observeOnly());
|
}
|
|
private static final class RecordingFactory implements DeviceAdapterFactory
|
{
|
private final List<StubAdapter> adapters = new ArrayList<>();
|
|
@Override
|
public String adapterType()
|
{
|
return "BTS";
|
}
|
|
@Override
|
public DeviceAdapter create(AdapterInstanceConfig config)
|
{
|
StubAdapter adapter = new StubAdapter(config.adapterCode());
|
adapters.add(adapter);
|
return adapter;
|
}
|
}
|
|
private static final class StubAdapter implements DeviceAdapter
|
{
|
private final String code;
|
private final AtomicBoolean closed = new AtomicBoolean();
|
|
private StubAdapter(String code)
|
{
|
this.code = code;
|
}
|
|
@Override
|
public String adapterCode()
|
{
|
return code;
|
}
|
|
@Override
|
public AdapterCapabilities capabilities()
|
{
|
return AdapterCapabilities.observeOnly();
|
}
|
|
@Override
|
public List<com.battery.device.model.DetectedChannel> discoverChannels()
|
{
|
return List.of();
|
}
|
|
@Override
|
public java.util.Map<ChannelIdentity, com.battery.device.model.RealtimeSnapshot> inquire(
|
java.util.Set<ChannelIdentity> channels)
|
{
|
return java.util.Map.of();
|
}
|
|
@Override
|
public List<com.battery.device.model.HistoryAvailability> inquireHistory(
|
java.util.Set<ChannelIdentity> channels)
|
{
|
return List.of();
|
}
|
|
@Override
|
public com.battery.device.model.HistoryPage downloadHistory(ChannelIdentity channel,
|
String btsTestId, long startPosition, int limit)
|
{
|
throw new UnsupportedOperationException();
|
}
|
|
@Override
|
public void control(ChannelIdentity channel, DeviceControlCommand command)
|
{
|
throw new UnsupportedOperationException();
|
}
|
|
@Override
|
public void close()
|
{
|
closed.set(true);
|
}
|
}
|
}
|