I'm making a web based scoring system for a robotic competition. When a point is scored, I want to refresh the page of everybody watching the game. My code is working "correctly".
My problem is that when I test and I open about 5 to 10 web pages, any other pages that I request are not processed until I close some pages. I think that what's happening is that request.startAsync() is not releasing the thread and it's waiting infinitely.
I've tested on both Jetty 9.2.7.v20150116 and Tomcat7. Both have the same slow behavior.
// Display a game with all it's events
// http://stackoverflow.com/questions/10878243/sse-and-servlet-3-0
#WebServlet(urlPatterns = { "/gameRefresh" }, asyncSupported = true)
public class GameRefreshController extends HttpServlet
{
private static final long serialVersionUID = -6890088129187673292L;
private static AtomicBoolean refreshNeeded = new AtomicBoolean();
private final Queue<AsyncContext> ongoingRequests = new ConcurrentLinkedQueue<>();
private ScheduledExecutorService service;
public static void setRefreshNeeded(boolean value)
{
refreshNeeded.set(value);
}
#Override
public void init(ServletConfig config) throws ServletException
{
final Runnable notifier = new Runnable()
{
#Override
public void run()
{
// Don't refresh if it's not needed.
if(!refreshNeeded.get())
{
return;
}
// This var is set by the backend when an event occurs.
setRefreshNeeded(false);
final Iterator<AsyncContext> iterator = ongoingRequests.iterator();
// not using for : in to allow removing items while iterating
while (iterator.hasNext())
{
AsyncContext asyncContext = iterator.next();
final ServletResponse servletResponse = asyncContext.getResponse();
PrintWriter out;
try
{
out = servletResponse.getWriter();
String toOutput = "data: refresh\n\n";
out.write(toOutput);
out.checkError();
}
catch(IOException exception)
{
// iterator is always removed because we refresh the whole page.
}
finally
{
iterator.remove();
}
}
}
};
service = Executors.newScheduledThreadPool(1);
service.scheduleAtFixedRate(notifier, 1, 1, TimeUnit.SECONDS);
}
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
response.setContentType("text/event-stream");
response.setCharacterEncoding("UTF-8");
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
final AsyncContext asyncContext = request.startAsync();
asyncContext.setTimeout(0);
asyncContext.addListener(new AsyncListener()
{
#Override
public void onComplete(AsyncEvent event) throws IOException
{
ongoingRequests.remove(asyncContext);
}
#Override
public void onTimeout(AsyncEvent event) throws IOException
{
ongoingRequests.remove(asyncContext);
}
#Override
public void onError(AsyncEvent event) throws IOException
{
ongoingRequests.remove(asyncContext);
}
#Override
public void onStartAsync(AsyncEvent event) throws IOException
{
}
});
ongoingRequests.add(asyncContext);
}
}
Related
on android app, using Broadcastreceiver to handle the notification click.
public class NotificationReceiver extends BroadcastReceiver {
public void onReceive(final Context context, final Intent intent) {
final PendingResult asyncResult = goAsync();
ExecutorService executor = Executors.newSingleThreadExecutor();
asycTask(executor, new Runnable() {
#Override
public void run() {
handleAction(context, intent); //a length process
asyncResult.finish(); //<=== unit test throws exception, asyncResult is null
}
});
}
#VisibleForTesting
void asycTask(ExecutorService executor, final Runnable task) {
try {
executor.execute(task);
} catch (Throwable ex) {}
}
}
in the unit test
#Test
public void test_{
NotificationReceiver receiver = new NotificationReceiver();
final CountDownLatch latch = new CountDownLatch(1);
receiver.onReceive(application, intent);
latch.await(10, TimeUnit.SECONDS);
// verify
// ... ...
}
but it throws an exception because the asyncResult is null.
How to test when it uses doAsync()?
fond a way, there must be better one tho.
BroadcastReceiver.PendingResult pendingResultMock =
mock(BroadcastReceiver.PendingResult.class);
NotificationReceiver receiverSpy = spy(new NotificationReceiver());
doReturn(pendingResultMock).when(receiverSpy).goAsync();
I am getting an exception when I am running this code, I am closing the testContext in beforeEach and test Method.
The test execution timed out. Make sure your asynchronous code includes calls to either VertxTestContext#completeNow(), VertxTestContext#failNow() or Checkpoint#flag()
java.util.concurrent.TimeoutException: The test execution timed out. Make sure your asynchronous code includes calls to either VertxTestContext#completeNow(), VertxTestContext#failNow() or Checkpoint#flag()
at io.vertx.junit5.VertxExtension.joinActiveTestContexts(VertxExtension.java:230)
#DisplayName("Test Case Workflow")
#ExtendWith(VertxExtension.class)
public class OrchestrationDBVerticleTest {
// tag::prepare[]
private Vertx vertx;
private OrchestrationDBService service;
public static final String CONFIG_JDBC_URL = "test.jdbc.url";
public static final String CONFIG_JDBC_DRIVER_CLASS = "test.jdbc.driver_class";
public static final String CONFIG_JDBC_MAX_POOL_SIZE = "test.jdbc.max_pool_size";
#BeforeEach
public void prepare(VertxTestContext testContext) throws InterruptedException {
vertx = Vertx.vertx();
JsonObject config = new JsonObject()
.put("url", vertx.getOrCreateContext().config().getString(CONFIG_JDBC_URL, "jdbc:hsqldb:mem:testdb"))
.put("driver_class", vertx.getOrCreateContext().config().getString(CONFIG_JDBC_DRIVER_CLASS, "org.hsqldb.jdbcDriver"))
.put("max_pool_size", vertx.getOrCreateContext().config().getInteger(CONFIG_JDBC_MAX_POOL_SIZE, 30));
JsonObject dbConfig = new JsonObject().put("jdbcConfig", config);
vertx.deployVerticle(new OrchestrationDBVerticle(), new DeploymentOptions().setConfig(dbConfig),
testContext.succeeding(id -> {
service = OrchestrationDBService.createProxy(vertx, OrchestrationDBVerticle.CONFIG_ORCHESTRATION_DB_QUEUE);
testContext.completeNow();
}));
}
// end::prepare[]
// tag::finish[]
#AfterEach
public void finish(VertxTestContext testContext) {
System.out.println("after");
vertx.close();
}
// end::finish[]
// tag::crud[]
#Test
public void crud_operations(VertxTestContext testContext) {
// Checkpoint callProxy = testContext.checkpoint();
JsonObject jobInput = (new JsonObject()).put("requestInput", new JsonObject().put("test", "test"))
.put("workflow", "WorkFlowHandler");
service.saveJobDetails(jobInput, testContext.succeeding(response -> {
System.out.println("Service Response : " + response);
Assertions.assertThat(response.toString().contains("IN_QUEUE"));
testContext.completeNow();
// callProxy.flag();
}));
}
// end::crud[]
}
EDIT: -
I was not completing testContext in the finish method.
#AfterEach
public void finish(VertxTestContext testContext) {
System.out.println("after");
vertx.close(testContext.succeeding(response -> {
testContext.completeNow();
}));
}
But Even after that my asserstion condtions are always true if I provide wrong input.
Solved.
I was not completing testContext in the finish method.
#AfterEach
public void finish(VertxTestContext testContext) {
System.out.println("after");
vertx.close(testContext.succeeding(response -> {
testContext.completeNow();
}));
}
Update:- Verifying the assertions in testContext.verify()
#Test
#DisplayName("🚀 Return Exact request what we stored in DB")
public void crud_operations(VertxTestContext testContext) {
JsonObject jobInput = (new JsonObject()).put("requestInput", new JsonObject().put("test", "test"))
.put("workflow", "CaseWorkFlowHandler");
service.saveJobDetails(jobInput, testContext.succeeding(response -> {
testContext.verify(() -> {
Assertions.assertThat(response.getJsonArray("rows").getJsonObject(0).getString("REQUEST_INPUT")).isEqualTo("{\"test\":\"test\"}");
});
testContext.completeNow();
}));
}
I'm trying to refactor one pretty old project, so I started implementing new architecture (MVVM) with Dagger2, RxJava, RxAndroid... Now everything is connected and working fine, now the problem is, I have no idea how to write a Unit test for my ViewModel..
I want to start with Login screen first, so I created a LoginViewModel, but first let me show you what I did..
I have a DataModule that provides 2 classes, RestApiRepository and ViewModelFactory. RestApiRepository looks like this:
public class RestApiRepository {
private RestClient restClient;
public RestApiRepository(RestClient restClient) {
this.restClient = restClient;
}
public Observable<AuthResponseEntity> authenticate(String header, AuthRequestEntity requestEntity) {
return restClient.postAuthObservable(header, requestEntity);
}
}
Rest client with api call for login:
public interface RestClient {
#POST(AUTH_URL)
Observable<AuthResponseEntity> postAuthObservable(#Header("Authorization") String authKey, #Body AuthRequestEntity requestEntity);
}
Second class from DataModule is ViewModelFactory:
#Singleton
public class ViewModelFactory extends ViewModelProvider.NewInstanceFactory implements ViewModelProvider.Factory {
private RestApiRepository repository;
#Inject
public ViewModelFactory(RestApiRepository repository) {
this.repository = repository;
}
#NonNull
#Override
public <T extends ViewModel> T create(#NonNull Class<T> modelClass) {
if (modelClass.isAssignableFrom(LoginViewModel.class)) {
return (T) new LoginViewModel(repository);
}
throw new IllegalArgumentException("Unknown class name");
}
}
And finally, LoginViewModel:
public class LoginViewModel extends ViewModel {
private final CompositeDisposable disposable = new CompositeDisposable();
private final MutableLiveData<AuthResponseEntity> responseLiveData = new MutableLiveData<>();
private RestApiRepository restApiRepository;
private SchedulerProvider provider;
public LoginViewModel(RestApiRepository restApiRepository, SchedulerProvider provider) {
this.restApiRepository = restApiRepository;
this.provider = provider;
}
public MutableLiveData<AuthResponseEntity> getResponseLiveData() {
return responseLiveData;
}
#Override
protected void onCleared() {
disposable.clear();
}
public void auth(String token, AuthRequestEntity requestEntity) {
if (token != null && requestEntity != null) {
disposable.add(restApiRepository.authenticate(token, requestEntity)
.subscribeOn(provider.io())
.observeOn(provider.ui())
.subscribeWith(new DisposableObserver<AuthResponseEntity>() {
#Override
public void onNext(AuthResponseEntity authResponseEntity) {
responseLiveData.setValue(authResponseEntity);
}
#Override
public void onError(Throwable e) {
AuthResponseEntity authResponseEntity = new AuthResponseEntity();
authResponseEntity.setErrorMessage(e.getMessage());
responseLiveData.setValue(authResponseEntity);
}
#Override
public void onComplete() {
}
}
));
}
}
}
So, I'm sure everything is connected well, I can successfuly login...
For the RxAndroid test issues, I found somewhere that I have to use this Scheduler provider like this:
public class AppSchedulerProvider implements SchedulerProvider {
public AppSchedulerProvider() {
}
#Override
public Scheduler computation() {
return Schedulers.trampoline();
}
#Override
public Scheduler io() {
return Schedulers.trampoline();
}
#Override
public Scheduler ui() {
return Schedulers.trampoline();
}
}
Below is my LoginViewModelTest class, but I don't know how to handle RxJava/RxAndroid inside the tests..
#RunWith(MockitoJUnitRunner.class)
public class LoginViewModelTest {
#Mock
private RestApiRepository restApiRepository;
#Mock
private MutableLiveData<AuthResponseEntity> mutableLiveData;
private LoginViewModel loginViewModel;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
AppSchedulerProvider schedulerProvider = new AppSchedulerProvider();
loginViewModel = Mockito.spy(new LoginViewModel(restApiRepository, schedulerProvider));
}
#Test
public void authenticate_error() {
String token = "token";
AuthRequestEntity requestEntity = Mockito.mock(AuthRequestEntity.class);
Mockito.doReturn(Observable.error(new Throwable())).when(restApiRepository).authenticate(token, requestEntity);
loginViewModel.auth(token, requestEntity);
AuthResponseEntity responseEntity = Mockito.mock(AuthResponseEntity.class);
responseEntity.setErrorMessage("Error");
Mockito.verify(mutableLiveData).setValue(responseEntity);
}
}
So, I wanted to write a test for failed case when onError is called, but when I run it, I get this error:
exclude patterns:io.reactivex.exceptions.UndeliverableException: The exception could not be delivered to the consumer because it has already canceled/disposed the flow or the exception has nowhere to go to begin with. Further reading: https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling | java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked. See http://g.co/androidstudio/not-mocked for details.
You can mock the behaviour of restApiRepository:
Mockito.when(restApiRepository.authenticate(token, requestEntity)).thenReturn(Observable.error(error));
and verify that responseLiveData.setValue is being called with the appropriate parameters
The controller method I am testing
#GetMapping("/customers")
#ResponseBody
public DeferredResult<ResponseEntity<Resources<Resource<Customer>>>> getAllCustomers(
#PageableDefault(page = 0, size = 20) #SortDefault.SortDefaults({
#SortDefault(sort = "name", direction = Direction.ASC) }) Pageable pageable,
PagedResourcesAssembler<Customer> assembler, HttpServletRequest request) {
DeferredResult<ResponseEntity<Resources<Resource<Customer>>>> response = new DeferredResult<>(
Long.valueOf(1000000));
response.onTimeout(() -> response
.setErrorResult(ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT).body("Request timed out.")));
response.onError((Throwable t) -> {
response.setErrorResult(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("An error occured."));
});
ListenableFuture<Page<Customer>> future = customerService.findAll(pageable);
future.addCallback(new ListenableFutureCallback<Page<Customer>>() {
#Override
public void onSuccess(Page<Customer> result) {
Link self = new Link(
ServletUriComponentsBuilder.fromRequestUri(request).buildAndExpand().toUri().toString(),
"self");
LOGGER.debug("Generated Self Link {} for Customer Resource Collection", self.getHref());
if (result.hasContent())
response.setResult(
ResponseEntity.ok(assembler.toResource(result, customerResourceAssembler, self)));
else
response.setErrorResult(ResponseEntity.notFound());
LOGGER.debug("Returning Response with {} customers", result.getNumber());
}
#Override
public void onFailure(Throwable ex) {
LOGGER.error("Could not retrieve customers due to error", ex);
response.setErrorResult(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Could not save customers list due to server error."));
}
});
return response;
}
the unit test
#RunWith(SpringRunner.class)
#WebMvcTest(CustomerController.class)
#EnableSpringDataWebSupport
#Import({ CustomerResourceAssember.class, BranchResourceAssembler.class, InvoiceResourceAssembler.class,
CustomerAsyncService.class })
public class CustomerControllerTests {
#Autowired
private MockMvc mockMvc;
#Autowired
CustomerAsyncService customerService;
#MockBean
private CustomerRepository customerRepository;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testWhenNoCustomersThenReturnsEmptyHALDocument() throws Exception {
// Given
BDDMockito.given(customerRepository.findAll(PageRequest.of(0, 20)))
.willReturn(new PageImpl<Customer>(Collections.emptyList()));
// When
MvcResult result = mockMvc.perform(get("/customers").accept(MediaTypes.HAL_JSON_VALUE)).andDo(print())
.andExpect(request().asyncStarted())
.andExpect(request().asyncResult(new PageImpl<Customer>(Collections.emptyList()))).andReturn();
// Then
mockMvc.perform(asyncDispatch(result)).andExpect(status().isOk());
}
This test neve completes, doesn't even time out on my IDE, I have to kill it everytime I run it, if run the entire app however this /customers endpoint gives a 404 when there are no customers added to the application.
What do I need to do make sure this test completes, the CustomerService call ultimately calls CustomerRepository which I have mocked because I couldn't get my brains around how to mock the async call to service method. the customer service class is as follows
#Async
#Service
public class CustomerAsyncService {
private CustomerRepository customerRepository;
#Autowired
public CustomerAsyncService(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
#Transactional(readOnly = true, isolation = Isolation.SERIALIZABLE)
public ListenableFuture<Page<Customer>> findAll(Pageable pageable) {
return AsyncResult.forValue(customerRepository.findAll(pageable));
}
I was hoping mocking the Repository method would do the trick. How do I mock the async service call
My bad was using mocks wrongly, this worked
#RunWith(SpringRunner.class)
#WebMvcTest(CustomerController.class)
#Import({ CustomerResourceAssember.class, BranchResourceAssembler.class, InvoiceResourceAssembler.class,
CustomerAsyncService.class })
public class CustomerControllerTests {
#MockBean
private CustomerRepository customerRepository;
#InjectMocks
CustomerAsyncService customerService = new CustomerAsyncService(customerRepository);
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
JacksonTester.initFields(this, objectMapper);
}
#Test
public void testReturnsNotFoundForEmptyGetAllCustomersResult() throws Exception {
// Given
Page<Customer> emptyPage = new PageImpl<Customer>(Collections.emptyList());
BDDMockito.given(customerRepository.findAll(any(Pageable.class))).willReturn(emptyPage);
// When
MvcResult result = mockMvc.perform(get("/customers")).andExpect(request().asyncStarted()).andDo(print()).andReturn();
// Then
mockMvc.perform(asyncDispatch(result)).andDo(print()).andExpect(status().isNotFound());
}
}
I am just starting with Akka and have created a test application. In it I create a bunch of actors who create a scheduler to generate a heartbeat event. Upon another type of event, I cancel the scheduler with heartbeat.cancel();, but I'd like to restart it when another event occurs. If I recreate the scheduler I see that the memory consumption increases continuously.
The question then would be either how do I resume the scheduler or how do I dispose the scheduler properly.
This is the code for that Actor
public class Device extends UntypedActor {
enum CommunicationStatus{
OK,
FAIL,
UNKNOWN
}
private static class Heartbeat {
}
public final String deviceId;
private CommunicationStatus commStatus;
private Cancellable heartBeatScheduler;
public Device(String Id)
{
deviceId = Id;
commStatus = CommunicationStatus.UNKNOWN;
}
#Override
public void preStart() {
getContext().system().eventStream().subscribe(getSelf(), DeviceCommunicationStatusUpdated.class);
startHeartbeat();
}
#Override
public void postStop() {
stopHeartBeat();
}
private void startHeartbeat() {
LoggingAdapter log = Logging.getLogger(getContext().system(), this);
log.info("Starting heartbeat");
heartBeatScheduler = getContext().system().scheduler().
schedule(Duration.Zero(),
Duration.create(1, TimeUnit.SECONDS),
getContext().self(),
new Heartbeat(),
getContext().system().dispatcher(),
ActorRef.noSender());
}
private void stopHeartBeat() {
if(!heartBeatScheduler.isCancelled()) {
LoggingAdapter log = Logging.getLogger(getContext().system(), this);
log.info("Stopping heartbeat");
heartBeatScheduler.cancel();
}
}
public String getDeviceId() {
return deviceId;
}
public CommunicationStatus getCommunicationStatus(){
return commStatus;
}
#Override
public void onReceive(Object message) throws Exception {
LoggingAdapter log = Logging.getLogger(getContext().system(), this);
if(message instanceof Heartbeat){
log.info("Pum, pum");
}
else if (message instanceof DeviceCommunicationStatusUpdated){
DeviceCommunicationStatusUpdated event = (DeviceCommunicationStatusUpdated) message;
if(event.deviceId == this.deviceId){
log.info("Received communication status update. '{}' is now {}", deviceId, event.state);
this.commStatus =
event.state == DeviceCommunicationStatusUpdated.State.OK ?
CommunicationStatus.OK : CommunicationStatus.FAIL;
if(commStatus == CommunicationStatus.OK && heartBeatScheduler.isCancelled()){
startHeartbeat();
}
else {
stopHeartBeat();
}
}
}
else unhandled(message);
}
}
Finally there is no leak, it's just that I'm new to Java and was impatient with the garbage collection. In any case, I would like to know about the resetting / restarting of a scheduler.