Best Selenium code snippet using org.openqa.selenium.grid.sessionqueue.local.LocalNewSessionQueue.complete
Source:DistributorTest.java  
1// Licensed to the Software Freedom Conservancy (SFC) under one2// or more contributor license agreements.  See the NOTICE file3// distributed with this work for additional information4// regarding copyright ownership.  The SFC licenses this file5// to you under the Apache License, Version 2.0 (the6// "License"); you may not use this file except in compliance7// with the License.  You may obtain a copy of the License at8//9//   http://www.apache.org/licenses/LICENSE-2.010//11// Unless required by applicable law or agreed to in writing,12// software distributed under the License is distributed on an13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY14// KIND, either express or implied.  See the License for the15// specific language governing permissions and limitations16// under the License.17package org.openqa.selenium.grid.distributor;18import com.google.common.collect.ImmutableMap;19import com.google.common.collect.ImmutableSet;20import org.junit.Assert;21import org.junit.Before;22import org.junit.Ignore;23import org.junit.Test;24import org.openqa.selenium.Capabilities;25import org.openqa.selenium.ImmutableCapabilities;26import org.openqa.selenium.MutableCapabilities;27import org.openqa.selenium.NoSuchSessionException;28import org.openqa.selenium.SessionNotCreatedException;29import org.openqa.selenium.TimeoutException;30import org.openqa.selenium.events.EventBus;31import org.openqa.selenium.events.local.GuavaEventBus;32import org.openqa.selenium.grid.data.Availability;33import org.openqa.selenium.grid.data.CreateSessionRequest;34import org.openqa.selenium.grid.data.CreateSessionResponse;35import org.openqa.selenium.grid.data.DistributorStatus;36import org.openqa.selenium.grid.data.NodeDrainComplete;37import org.openqa.selenium.grid.data.NodeHeartBeatEvent;38import org.openqa.selenium.grid.data.NodeStatus;39import org.openqa.selenium.grid.data.Session;40import org.openqa.selenium.grid.data.Slot;41import org.openqa.selenium.grid.distributor.local.LocalDistributor;42import org.openqa.selenium.grid.distributor.remote.RemoteDistributor;43import org.openqa.selenium.grid.node.HealthCheck;44import org.openqa.selenium.grid.node.Node;45import org.openqa.selenium.grid.node.local.LocalNode;46import org.openqa.selenium.grid.security.Secret;47import org.openqa.selenium.grid.sessionmap.SessionMap;48import org.openqa.selenium.grid.sessionmap.local.LocalSessionMap;49import org.openqa.selenium.grid.sessionqueue.local.LocalNewSessionQueue;50import org.openqa.selenium.grid.sessionqueue.local.LocalNewSessionQueuer;51import org.openqa.selenium.grid.testing.EitherAssert;52import org.openqa.selenium.grid.testing.PassthroughHttpClient;53import org.openqa.selenium.grid.testing.TestSessionFactory;54import org.openqa.selenium.grid.web.CombinedHandler;55import org.openqa.selenium.internal.Either;56import org.openqa.selenium.net.PortProber;57import org.openqa.selenium.remote.Dialect;58import org.openqa.selenium.remote.NewSessionPayload;59import org.openqa.selenium.remote.SessionId;60import org.openqa.selenium.remote.http.HttpClient;61import org.openqa.selenium.remote.http.HttpHandler;62import org.openqa.selenium.remote.http.HttpRequest;63import org.openqa.selenium.remote.http.HttpResponse;64import org.openqa.selenium.remote.tracing.DefaultTestTracer;65import org.openqa.selenium.remote.tracing.Tracer;66import org.openqa.selenium.support.ui.FluentWait;67import org.openqa.selenium.support.ui.Wait;68import java.io.IOException;69import java.io.UncheckedIOException;70import java.net.MalformedURLException;71import java.net.URI;72import java.net.URISyntaxException;73import java.net.URL;74import java.time.Duration;75import java.time.Instant;76import java.util.HashMap;77import java.util.HashSet;78import java.util.Map;79import java.util.Set;80import java.util.UUID;81import java.util.concurrent.CountDownLatch;82import java.util.concurrent.TimeUnit;83import java.util.concurrent.atomic.AtomicBoolean;84import java.util.concurrent.atomic.AtomicReference;85import java.util.logging.Logger;86import java.util.stream.Collectors;87import static org.assertj.core.api.Assertions.assertThat;88import static org.assertj.core.api.Assertions.fail;89import static org.junit.Assert.assertFalse;90import static org.junit.Assert.assertTrue;91import static org.openqa.selenium.grid.data.Availability.DOWN;92import static org.openqa.selenium.grid.data.Availability.UP;93import static org.openqa.selenium.remote.http.Contents.utf8String;94import static org.openqa.selenium.remote.http.HttpMethod.POST;95public class DistributorTest {96  private static final Logger LOG = Logger.getLogger("Distributor Test");97  private final Secret registrationSecret = new Secret("hellim");98  private final Wait<Object> wait = new FluentWait<>(new Object()).withTimeout(Duration.ofSeconds(5));99  private Tracer tracer;100  private EventBus bus;101  private Distributor local;102  private Capabilities stereotype;103  private Capabilities caps;104  private URI nodeUri;105  private URI routableUri;106  private static <A, B> EitherAssert<A, B> assertThatEither(Either<A, B> either) {107    return new EitherAssert<>(either);108  }109  @Before110  public void setUp() throws URISyntaxException {111    nodeUri = new URI("http://example:5678");112    routableUri = new URI("http://localhost:1234");113    tracer = DefaultTestTracer.createTracer();114    bus = new GuavaEventBus();115    LocalSessionMap sessions = new LocalSessionMap(tracer, bus);116    LocalNewSessionQueue localNewSessionQueue = new LocalNewSessionQueue(117      tracer,118      bus,119      Duration.ofSeconds(2),120      Duration.ofSeconds(2));121    LocalNewSessionQueuer queuer = new LocalNewSessionQueuer(122      tracer,123      bus,124      localNewSessionQueue,125      registrationSecret);126    local = new LocalDistributor(127      tracer,128      bus,129      HttpClient.Factory.createDefault(),130      sessions,131      queuer,132      registrationSecret,133      Duration.ofMinutes(5));134    stereotype = new ImmutableCapabilities("browserName", "cheese");135    caps = new ImmutableCapabilities("browserName", "cheese");136  }137  @Test138  public void creatingANewSessionWithoutANodeEndsInFailure() {139    try (NewSessionPayload payload = NewSessionPayload.create(caps)) {140      Either<SessionNotCreatedException, CreateSessionResponse> result =141        local.newSession(createRequest(payload));142      assertThatEither(result).isLeft();143    }144  }145  @Test146  public void shouldStartHeartBeatOnNodeRegistration() {147    EventBus bus = new GuavaEventBus();148    LocalSessionMap sessions = new LocalSessionMap(tracer, bus);149    LocalNewSessionQueue localNewSessionQueue = new LocalNewSessionQueue(150      tracer,151      bus,152      Duration.ofSeconds(2),153      Duration.ofSeconds(2));154    LocalNewSessionQueuer queuer = new LocalNewSessionQueuer(155      tracer,156      bus,157      localNewSessionQueue,158      registrationSecret);159    LocalNode node = LocalNode.builder(tracer, bus, routableUri, routableUri, registrationSecret)160      .add(161        caps,162        new TestSessionFactory((id, c) -> new Session(id, nodeUri, stereotype, c, Instant.now())))163      .heartbeatPeriod(Duration.ofSeconds(10))164      .build();165    Distributor distributor = new LocalDistributor(166      tracer,167      bus,168      new PassthroughHttpClient.Factory(node),169      sessions,170      queuer,171      registrationSecret,172      Duration.ofMinutes(5));173    distributor.add(node);174    AtomicBoolean heartbeatStarted = new AtomicBoolean();175    CountDownLatch latch = new CountDownLatch(1);176    bus.addListener(NodeHeartBeatEvent.listener(nodeStatus -> {177      latch.countDown();178      if (node.getId().equals(nodeStatus.getId())) {179        heartbeatStarted.set(true);180      }181    }));182    waitToHaveCapacity(distributor);183    boolean eventFired = false;184    try {185      eventFired = latch.await(30, TimeUnit.SECONDS);186    } catch (InterruptedException e) {187      Assert.fail("Thread Interrupted");188    }189    assertThat(eventFired).isTrue();190    assertThat(heartbeatStarted).isTrue();191  }192  @Test193  public void shouldBeAbleToAddANodeAndCreateASession() {194    LocalSessionMap sessions = new LocalSessionMap(tracer, bus);195    LocalNewSessionQueue localNewSessionQueue = new LocalNewSessionQueue(196      tracer,197      bus,198      Duration.ofSeconds(2),199      Duration.ofSeconds(2));200    LocalNewSessionQueuer queuer = new LocalNewSessionQueuer(201      tracer,202      bus,203      localNewSessionQueue,204      registrationSecret);205    LocalNode node = LocalNode.builder(tracer, bus, routableUri, routableUri, registrationSecret)206      .add(207        caps,208        new TestSessionFactory((id, c) -> new Session(id, nodeUri, stereotype, c, Instant.now())))209      .build();210    Distributor distributor = new LocalDistributor(211      tracer,212      bus,213      new PassthroughHttpClient.Factory(node),214      sessions,215      queuer,216      registrationSecret,217      Duration.ofMinutes(5));218    distributor.add(node);219    waitToHaveCapacity(distributor);220    MutableCapabilities sessionCaps = new MutableCapabilities(caps);221    sessionCaps.setCapability("sausages", "gravy");222    try (NewSessionPayload payload = NewSessionPayload.create(sessionCaps)) {223      Either<SessionNotCreatedException, CreateSessionResponse> result =224        distributor.newSession(createRequest(payload));225      assertThatEither(result).isRight();226      Session session = result.right().getSession();227      assertThat(session.getCapabilities()).isEqualTo(sessionCaps);228      assertThat(session.getUri()).isEqualTo(routableUri);229    }230  }231  @Test232  public void creatingASessionAddsItToTheSessionMap() {233    LocalSessionMap sessions = new LocalSessionMap(tracer, bus);234    LocalNewSessionQueue localNewSessionQueue = new LocalNewSessionQueue(235      tracer,236      bus,237      Duration.ofSeconds(2),238      Duration.ofSeconds(2));239    LocalNewSessionQueuer queuer = new LocalNewSessionQueuer(240      tracer,241      bus,242      localNewSessionQueue,243      registrationSecret);244    LocalNode node = LocalNode.builder(tracer, bus, routableUri, routableUri, registrationSecret)245      .add(246        caps,247        new TestSessionFactory((id, c) -> new Session(id, nodeUri, stereotype, c, Instant.now())))248      .build();249    LocalDistributor distributor = new LocalDistributor(250      tracer,251      bus,252      new PassthroughHttpClient.Factory(node),253      sessions,254      queuer,255      registrationSecret,256      Duration.ofMinutes(5));257    distributor.add(node);258    waitToHaveCapacity(distributor);259    MutableCapabilities sessionCaps = new MutableCapabilities(caps);260    sessionCaps.setCapability("sausages", "gravy");261    try (NewSessionPayload payload = NewSessionPayload.create(sessionCaps)) {262      Either<SessionNotCreatedException, CreateSessionResponse> result =263        distributor.newSession(createRequest(payload));264      assertThatEither(result).isRight();265      Session returned = result.right().getSession();266      Session session = sessions.get(returned.getId());267      assertThat(session.getCapabilities()).isEqualTo(sessionCaps);268      assertThat(session.getUri()).isEqualTo(routableUri);269    }270  }271  @Test272  public void shouldBeAbleToRemoveANode() throws MalformedURLException {273    LocalSessionMap sessions = new LocalSessionMap(tracer, bus);274    LocalNewSessionQueue localNewSessionQueue = new LocalNewSessionQueue(275      tracer,276      bus,277      Duration.ofSeconds(2),278      Duration.ofSeconds(2));279    LocalNewSessionQueuer queuer = new LocalNewSessionQueuer(280      tracer,281      bus,282      localNewSessionQueue,283      registrationSecret);284    LocalNode node = LocalNode.builder(tracer, bus, routableUri, routableUri, registrationSecret)285      .add(286        caps,287        new TestSessionFactory((id, c) -> new Session(id, nodeUri, stereotype, c, Instant.now())))288      .build();289    Distributor local = new LocalDistributor(290      tracer,291      bus,292      new PassthroughHttpClient.Factory(node),293      sessions,294      queuer,295      registrationSecret,296      Duration.ofMinutes(5));297    Distributor distributor = new RemoteDistributor(298      tracer,299      new PassthroughHttpClient.Factory(local),300      new URL("http://does.not.exist"),301      registrationSecret);302    distributor.add(node);303    distributor.remove(node.getId());304    try (NewSessionPayload payload = NewSessionPayload.create(caps)) {305      Either<SessionNotCreatedException, CreateSessionResponse> result =306        local.newSession(createRequest(payload));307      assertThatEither(result).isLeft();308    }309  }310  @Test311  public void testDrainingNodeDoesNotAcceptNewSessions() {312    SessionMap sessions = new LocalSessionMap(tracer, bus);313    LocalNewSessionQueue localNewSessionQueue = new LocalNewSessionQueue(314      tracer,315      bus,316      Duration.ofSeconds(2),317      Duration.ofSeconds(2));318    LocalNewSessionQueuer queuer = new LocalNewSessionQueuer(319      tracer,320      bus,321      localNewSessionQueue,322      registrationSecret);323    LocalNode node = LocalNode.builder(tracer, bus, routableUri, routableUri, registrationSecret)324      .add(325        caps,326        new TestSessionFactory((id, c) -> new Session(id, nodeUri, stereotype, c, Instant.now())))327      .build();328    Distributor distributor = new LocalDistributor(329      tracer,330      bus,331      new PassthroughHttpClient.Factory(node),332      sessions,333      queuer,334      registrationSecret,335      Duration.ofMinutes(5));336    distributor.add(node);337    distributor.drain(node.getId());338    assertTrue(node.isDraining());339    NewSessionPayload payload = NewSessionPayload.create(caps);340    Either<SessionNotCreatedException, CreateSessionResponse> result =341      distributor.newSession(createRequest(payload));342    assertThatEither(result).isLeft();343  }344  @Test345  public void testDrainedNodeShutsDownOnceEmpty() throws InterruptedException {346    SessionMap sessions = new LocalSessionMap(tracer, bus);347    LocalNewSessionQueue localNewSessionQueue = new LocalNewSessionQueue(348      tracer,349      bus,350      Duration.ofSeconds(2),351      Duration.ofSeconds(2));352    LocalNewSessionQueuer queuer = new LocalNewSessionQueuer(353      tracer,354      bus,355      localNewSessionQueue,356      registrationSecret);357    LocalNode node = LocalNode.builder(tracer, bus, routableUri, routableUri, registrationSecret)358      .add(359        caps,360        new TestSessionFactory((id, c) -> new Session(id, nodeUri, stereotype, c, Instant.now())))361      .build();362    CountDownLatch latch = new CountDownLatch(1);363    bus.addListener(NodeDrainComplete.listener(ignored -> latch.countDown()));364    Distributor distributor = new LocalDistributor(365      tracer,366      bus,367      new PassthroughHttpClient.Factory(node),368      sessions,369      queuer,370      registrationSecret,371      Duration.ofMinutes(5));372    distributor.add(node);373    waitToHaveCapacity(distributor);374    distributor.drain(node.getId());375    latch.await(5, TimeUnit.SECONDS);376    assertThat(latch.getCount()).isEqualTo(0);377    assertThat(distributor.getAvailableNodes().size()).isEqualTo(0);378    try (NewSessionPayload payload = NewSessionPayload.create(caps)) {379      Either<SessionNotCreatedException, CreateSessionResponse> result =380        distributor.newSession(createRequest(payload));381      assertThatEither(result).isLeft();382    }383  }384  @Test385  public void drainedNodeDoesNotShutDownIfNotEmpty() throws InterruptedException {386    SessionMap sessions = new LocalSessionMap(tracer, bus);387    LocalNewSessionQueue localNewSessionQueue = new LocalNewSessionQueue(388      tracer,389      bus,390      Duration.ofSeconds(2),391      Duration.ofSeconds(2));392    LocalNewSessionQueuer queuer = new LocalNewSessionQueuer(393      tracer,394      bus,395      localNewSessionQueue,396      registrationSecret);397    LocalNode node = LocalNode.builder(tracer, bus, routableUri, routableUri, registrationSecret)398      .add(399        caps,400        new TestSessionFactory((id, c) -> new Session(id, nodeUri, stereotype, c, Instant.now())))401      .build();402    CountDownLatch latch = new CountDownLatch(1);403    bus.addListener(NodeDrainComplete.listener(ignored -> latch.countDown()));404    Distributor distributor = new LocalDistributor(405      tracer,406      bus,407      new PassthroughHttpClient.Factory(node),408      sessions,409      queuer,410      registrationSecret,411      Duration.ofMinutes(5));412    distributor.add(node);413    waitToHaveCapacity(distributor);414    NewSessionPayload payload = NewSessionPayload.create(caps);415    Either<SessionNotCreatedException, CreateSessionResponse> session =416      distributor.newSession(createRequest(payload));417    assertThatEither(session).isRight();418    distributor.drain(node.getId());419    latch.await(5, TimeUnit.SECONDS);420    assertThat(latch.getCount()).isEqualTo(1);421    assertThat(distributor.getAvailableNodes().size()).isEqualTo(1);422  }423  @Test424  public void drainedNodeShutsDownAfterSessionsFinish() throws InterruptedException {425    SessionMap sessions = new LocalSessionMap(tracer, bus);426    LocalNewSessionQueue localNewSessionQueue = new LocalNewSessionQueue(427      tracer,428      bus,429      Duration.ofSeconds(2),430      Duration.ofSeconds(2));431    LocalNewSessionQueuer queuer = new LocalNewSessionQueuer(432      tracer,433      bus,434      localNewSessionQueue,435      registrationSecret);436    LocalNode node = LocalNode.builder(tracer, bus, routableUri, routableUri, registrationSecret)437      .add(438        caps,439        new TestSessionFactory((id, c) -> new Session(id, nodeUri, stereotype, c, Instant.now())))440      .add(441        caps,442        new TestSessionFactory((id, c) -> new Session(id, nodeUri, stereotype, c, Instant.now())))443      .build();444    CountDownLatch latch = new CountDownLatch(1);445    bus.addListener(NodeDrainComplete.listener(ignored -> latch.countDown()));446    Distributor distributor = new LocalDistributor(447      tracer,448      bus,449      new PassthroughHttpClient.Factory(node),450      sessions,451      queuer,452      registrationSecret,453      Duration.ofMinutes(5));454    distributor.add(node);455    waitToHaveCapacity(distributor);456    NewSessionPayload payload = NewSessionPayload.create(caps);457    Either<SessionNotCreatedException, CreateSessionResponse> firstResponse =458      distributor.newSession(createRequest(payload));459    Either<SessionNotCreatedException, CreateSessionResponse> secondResponse =460      distributor.newSession(createRequest(payload));461    distributor.drain(node.getId());462    assertThat(distributor.getAvailableNodes().size()).isEqualTo(1);463    node.stop(firstResponse.right().getSession().getId());464    node.stop(secondResponse.right().getSession().getId());465    latch.await(5, TimeUnit.SECONDS);466    assertThat(latch.getCount()).isEqualTo(0);467    assertThat(distributor.getAvailableNodes().size()).isEqualTo(0);468  }469  @Test470  public void registeringTheSameNodeMultipleTimesOnlyCountsTheFirstTime() {471    LocalNode node = LocalNode.builder(tracer, bus, routableUri, routableUri, registrationSecret)472      .add(473        caps,474        new TestSessionFactory((id, c) -> new Session(id, nodeUri, stereotype, c, Instant.now())))475      .build();476    local.add(node);477    local.add(node);478    DistributorStatus status = local.getStatus();479    assertThat(status.getNodes().size()).isEqualTo(1);480  }481  @Test482  public void theMostLightlyLoadedNodeIsSelectedFirst() {483    // Create enough hosts so that we avoid the scheduler returning hosts in:484    // * insertion order485    // * reverse insertion order486    // * sorted with most heavily used first487    SessionMap sessions = new LocalSessionMap(tracer, bus);488    LocalNewSessionQueue localNewSessionQueue = new LocalNewSessionQueue(489      tracer,490      bus,491      Duration.ofSeconds(2),492      Duration.ofSeconds(2));493    LocalNewSessionQueuer queuer = new LocalNewSessionQueuer(494      tracer,495      bus,496      localNewSessionQueue,497      registrationSecret);498    Node lightest = createNode(caps, 10, 0);499    Node medium = createNode(caps, 10, 4);500    Node heavy = createNode(caps, 10, 6);501    Node massive = createNode(caps, 10, 8);502    CombinedHandler handler = new CombinedHandler();503    handler.addHandler(lightest);504    handler.addHandler(medium);505    handler.addHandler(heavy);506    handler.addHandler(massive);507    Distributor distributor = new LocalDistributor(508      tracer,509      bus,510      new PassthroughHttpClient.Factory(handler),511      sessions,512      queuer,513      registrationSecret,514      Duration.ofMinutes(5))515      .add(heavy)516      .add(medium)517      .add(lightest)518      .add(massive);519    wait.until(obj -> distributor.getStatus().getNodes().size() == 4);520    wait.until(obj -> distributor.getStatus().hasCapacity());521    try (NewSessionPayload payload = NewSessionPayload.create(caps)) {522      Either<SessionNotCreatedException, CreateSessionResponse> result =523        distributor.newSession(createRequest(payload));524      assertThatEither(result).isRight();525      Session session = result.right().getSession();526      assertThat(session.getUri()).isEqualTo(lightest.getStatus().getUri());527    }528  }529  @Test530  public void shouldUseLastSessionCreatedTimeAsTieBreaker() {531    SessionMap sessions = new LocalSessionMap(tracer, bus);532    LocalNewSessionQueue localNewSessionQueue = new LocalNewSessionQueue(533      tracer,534      bus,535      Duration.ofSeconds(2),536      Duration.ofSeconds(2));537    LocalNewSessionQueuer queuer = new LocalNewSessionQueuer(538      tracer,539      bus,540      localNewSessionQueue,541      registrationSecret);542    Node leastRecent = createNode(caps, 5, 0);543    CombinedHandler handler = new CombinedHandler();544    handler.addHandler(sessions);545    handler.addHandler(leastRecent);546    Distributor distributor = new LocalDistributor(547      tracer,548      bus,549      new PassthroughHttpClient.Factory(handler),550      sessions,551      queuer,552      registrationSecret,553      Duration.ofMinutes(5))554      .add(leastRecent);555    waitToHaveCapacity(distributor);556    try (NewSessionPayload payload = NewSessionPayload.create(caps)) {557      distributor.newSession(createRequest(payload));558      // Will be "leastRecent" by default559    }560    Node middle = createNode(caps, 5, 0);561    handler.addHandler(middle);562    distributor.add(middle);563    waitForAllNodesToHaveCapacity(distributor, 2);564    try (NewSessionPayload payload = NewSessionPayload.create(caps)) {565      Either<SessionNotCreatedException, CreateSessionResponse> result =566        distributor.newSession(createRequest(payload));567      assertThatEither(result).isRight();568      Session session = result.right().getSession();569      // Least lightly loaded is middle570      assertThat(session.getUri()).isEqualTo(middle.getStatus().getUri());571    }572    Node mostRecent = createNode(caps, 5, 0);573    handler.addHandler(mostRecent);574    distributor.add(mostRecent);575    waitForAllNodesToHaveCapacity(distributor, 3);576    try (NewSessionPayload payload = NewSessionPayload.create(caps)) {577      Either<SessionNotCreatedException, CreateSessionResponse> result =578        distributor.newSession(createRequest(payload));579      assertThatEither(result).isRight();580      Session session = result.right().getSession();581      // Least lightly loaded is most recent582      assertThat(session.getUri()).isEqualTo(mostRecent.getStatus().getUri());583    }584    // All the nodes should be equally loaded.585    Map<Capabilities, Integer> expected = getFreeStereotypeCounts(mostRecent.getStatus());586    assertThat(getFreeStereotypeCounts(leastRecent.getStatus())).isEqualTo(expected);587    assertThat(getFreeStereotypeCounts(middle.getStatus())).isEqualTo(expected);588    // All nodes are now equally loaded. We should be going in time order now589    try (NewSessionPayload payload = NewSessionPayload.create(caps)) {590      Either<SessionNotCreatedException, CreateSessionResponse> result =591        distributor.newSession(createRequest(payload));592      assertThatEither(result).isRight();593      Session session = result.right().getSession();594      assertThat(session.getUri()).isEqualTo(leastRecent.getStatus().getUri());595    }596  }597  private Map<Capabilities, Integer> getFreeStereotypeCounts(NodeStatus status) {598    Map<Capabilities, Integer> toReturn = new HashMap<>();599    for (Slot slot : status.getSlots()) {600      int count = toReturn.getOrDefault(slot.getStereotype(), 0);601      count++;602      toReturn.put(slot.getStereotype(), count);603    }604    return toReturn;605  }606  @Test607  public void shouldIncludeHostsThatAreUpInHostList() {608    CombinedHandler handler = new CombinedHandler();609    SessionMap sessions = new LocalSessionMap(tracer, bus);610    LocalNewSessionQueue localNewSessionQueue = new LocalNewSessionQueue(611      tracer,612      bus,613      Duration.ofSeconds(2),614      Duration.ofSeconds(2));615    LocalNewSessionQueuer queuer = new LocalNewSessionQueuer(616      tracer,617      bus,618      localNewSessionQueue,619      registrationSecret);620    handler.addHandler(sessions);621    URI uri = createUri();622    Node alwaysDown = LocalNode.builder(tracer, bus, uri, uri, registrationSecret)623      .add(624        caps,625        new TestSessionFactory((id, c) -> new Session(id, uri, stereotype, c, Instant.now())))626      .advanced()627      .healthCheck(() -> new HealthCheck.Result(DOWN, "Boo!"))628      .build();629    handler.addHandler(alwaysDown);630    Node alwaysUp = LocalNode.builder(tracer, bus, uri, uri, registrationSecret)631      .add(632        caps,633        new TestSessionFactory((id, c) -> new Session(id, uri, stereotype, c, Instant.now())))634      .advanced()635      .healthCheck(() -> new HealthCheck.Result(UP, "Yay!"))636      .build();637    handler.addHandler(alwaysUp);638    LocalDistributor distributor = new LocalDistributor(639      tracer,640      bus,641      new PassthroughHttpClient.Factory(handler),642      sessions,643      queuer,644      registrationSecret,645      Duration.ofMinutes(5));646    handler.addHandler(distributor);647    distributor.add(alwaysDown);648    // Should be unable to create a session because the node is down.649    try (NewSessionPayload payload = NewSessionPayload.create(caps)) {650      Either<SessionNotCreatedException, CreateSessionResponse> result =651        distributor.newSession(createRequest(payload));652      assertThatEither(result).isLeft();653    }654    distributor.add(alwaysUp);655    waitToHaveCapacity(distributor);656    try (NewSessionPayload payload = NewSessionPayload.create(caps)) {657      Either<SessionNotCreatedException, CreateSessionResponse> result =658        distributor.newSession(createRequest(payload));659      assertThatEither(result).isRight();660    }661  }662  @Test663  public void shouldNotScheduleAJobIfAllSlotsAreBeingUsed() {664    SessionMap sessions = new LocalSessionMap(tracer, bus);665    LocalNewSessionQueue localNewSessionQueue = new LocalNewSessionQueue(666      tracer,667      bus,668      Duration.ofSeconds(2),669      Duration.ofSeconds(2));670    LocalNewSessionQueuer queuer = new LocalNewSessionQueuer(671      tracer,672      bus,673      localNewSessionQueue,674      registrationSecret);675    LocalNode node = LocalNode.builder(tracer, bus, routableUri, routableUri, registrationSecret)676      .add(caps, new TestSessionFactory((id, c) -> new Session(677        id, nodeUri, stereotype, c, Instant.now())))678      .build();679    Distributor distributor = new LocalDistributor(680      tracer,681      bus,682      new PassthroughHttpClient.Factory(node),683      sessions,684      queuer,685      registrationSecret,686      Duration.ofMinutes(5));687    distributor.add(node);688    waitToHaveCapacity(distributor);689    // Use up the one slot available690    try (NewSessionPayload payload = NewSessionPayload.create(caps)) {691      Either<SessionNotCreatedException, CreateSessionResponse> result =692        distributor.newSession(createRequest(payload));693      assertThatEither(result).isRight();694    }695    // Now try and create a session.696    try (NewSessionPayload payload = NewSessionPayload.create(caps)) {697      Either<SessionNotCreatedException, CreateSessionResponse> result =698        distributor.newSession(createRequest(payload));699      assertThatEither(result).isLeft();700    }701  }702  @Test703  public void shouldReleaseSlotOnceSessionEnds() {704    SessionMap sessions = new LocalSessionMap(tracer, bus);705    LocalNewSessionQueue localNewSessionQueue = new LocalNewSessionQueue(706      tracer,707      bus,708      Duration.ofSeconds(2),709      Duration.ofSeconds(2));710    LocalNewSessionQueuer queuer = new LocalNewSessionQueuer(711      tracer,712      bus,713      localNewSessionQueue,714      registrationSecret);715    LocalNode node = LocalNode.builder(tracer, bus, routableUri, routableUri, registrationSecret)716      .add(caps, new TestSessionFactory((id, c) -> new Session(717        id, nodeUri, stereotype, c, Instant.now())))718      .build();719    Distributor distributor = new LocalDistributor(720      tracer,721      bus,722      new PassthroughHttpClient.Factory(node),723      sessions,724      queuer,725      registrationSecret,726      Duration.ofMinutes(5));727    distributor.add(node);728    waitToHaveCapacity(distributor);729    // Use up the one slot available730    Session session;731    try (NewSessionPayload payload = NewSessionPayload.create(caps)) {732      Either<SessionNotCreatedException, CreateSessionResponse> result =733        distributor.newSession(createRequest(payload));734      assertThatEither(result).isRight();735      session = result.right().getSession();736      // Make sure the session map has the session737      sessions.get(session.getId());738      node.stop(session.getId());739      // Now wait for the session map to say the session is gone.740      wait.until(obj -> {741        try {742          sessions.get(session.getId());743          return false;744        } catch (NoSuchSessionException e) {745          return true;746        }747      });748    }749    waitToHaveCapacity(distributor);750    // And we should now be able to create another session.751    try (NewSessionPayload payload = NewSessionPayload.create(caps)) {752      Either<SessionNotCreatedException, CreateSessionResponse> result =753        distributor.newSession(createRequest(payload));754      assertThatEither(result).isRight();755    }756  }757  @Test758  public void shouldNotStartASessionIfTheCapabilitiesAreNotSupported() {759    CombinedHandler handler = new CombinedHandler();760    LocalSessionMap sessions = new LocalSessionMap(tracer, bus);761    LocalNewSessionQueue localNewSessionQueue = new LocalNewSessionQueue(762      tracer,763      bus,764      Duration.ofSeconds(2),765      Duration.ofSeconds(2));766    LocalNewSessionQueuer queuer = new LocalNewSessionQueuer(767      tracer,768      bus,769      localNewSessionQueue,770      registrationSecret);771    handler.addHandler(handler);772    Distributor distributor = new LocalDistributor(773      tracer,774      bus,775      new PassthroughHttpClient.Factory(handler),776      sessions,777      queuer,778      registrationSecret,779      Duration.ofMinutes(5));780    handler.addHandler(distributor);781    Node node = createNode(caps, 1, 0);782    handler.addHandler(node);783    distributor.add(node);784    waitToHaveCapacity(distributor);785    ImmutableCapabilities unmatched =786      new ImmutableCapabilities("browserName", "transit of venus");787    try (NewSessionPayload payload = NewSessionPayload.create(unmatched)) {788      Either<SessionNotCreatedException, CreateSessionResponse> result =789        distributor.newSession(createRequest(payload));790      assertThatEither(result).isLeft();791    }792  }793  @Test794  public void attemptingToStartASessionWhichFailsMarksAsTheSlotAsAvailable() {795    SessionMap sessions = new LocalSessionMap(tracer, bus);796    LocalNewSessionQueue localNewSessionQueue = new LocalNewSessionQueue(797      tracer,798      bus,799      Duration.ofSeconds(2),800      Duration.ofSeconds(2));801    LocalNewSessionQueuer queuer = new LocalNewSessionQueuer(802      tracer,803      bus,804      localNewSessionQueue,805      registrationSecret);806    LocalNode node = LocalNode.builder(tracer, bus, routableUri, routableUri, registrationSecret)807      .add(caps, new TestSessionFactory((id, caps) -> {808        throw new SessionNotCreatedException("OMG");809      }))810      .build();811    Distributor distributor = new LocalDistributor(812      tracer,813      bus,814      new PassthroughHttpClient.Factory(node),815      sessions,816      queuer,817      registrationSecret,818      Duration.ofMinutes(5));819    distributor.add(node);820    waitToHaveCapacity(distributor);821    try (NewSessionPayload payload = NewSessionPayload.create(caps)) {822      Either<SessionNotCreatedException, CreateSessionResponse> result =823        distributor.newSession(createRequest(payload));824      assertThatEither(result).isLeft();825    }826    assertThat(distributor.getStatus().hasCapacity()).isTrue();827  }828  @Test829  public void shouldReturnNodesThatWereDownToPoolOfNodesOnceTheyMarkTheirHealthCheckPasses() {830    CombinedHandler handler = new CombinedHandler();831    SessionMap sessions = new LocalSessionMap(tracer, bus);832    handler.addHandler(sessions);833    AtomicReference<Availability> isUp = new AtomicReference<>(DOWN);834    LocalNewSessionQueue localNewSessionQueue = new LocalNewSessionQueue(835      tracer,836      bus,837      Duration.ofSeconds(2),838      Duration.ofSeconds(2));839    LocalNewSessionQueuer queuer = new LocalNewSessionQueuer(840      tracer,841      bus,842      localNewSessionQueue,843      registrationSecret);844    URI uri = createUri();845    Node node = LocalNode.builder(tracer, bus, uri, uri, registrationSecret)846      .add(847        caps,848        new TestSessionFactory((id, caps) -> new Session(id, uri, stereotype, caps, Instant.now())))849      .advanced()850      .healthCheck(() -> new HealthCheck.Result(isUp.get(), "TL;DR"))851      .build();852    handler.addHandler(node);853    LocalDistributor distributor = new LocalDistributor(854      tracer,855      bus,856      new PassthroughHttpClient.Factory(handler),857      sessions,858      queuer,859      registrationSecret,860      Duration.ofMinutes(5));861    handler.addHandler(distributor);862    distributor.add(node);863    // Should be unable to create a session because the node is down.864    try (NewSessionPayload payload = NewSessionPayload.create(caps)) {865      Either<SessionNotCreatedException, CreateSessionResponse> result =866        distributor.newSession(createRequest(payload));867      assertThatEither(result).isLeft();868    }869    // Mark the node as being up870    isUp.set(UP);871    // Kick the machinery to ensure that everything is fine.872    distributor.refresh();873    // Because the node is now up and running, we should now be able to create a session874    try (NewSessionPayload payload = NewSessionPayload.create(caps)) {875      Either<SessionNotCreatedException, CreateSessionResponse> result =876        distributor.newSession(createRequest(payload));877      assertThatEither(result).isRight();878    }879  }880  private Set<Node> createNodeSet(Distributor distributor, int count, Capabilities...capabilities) {881    Set<Node> nodeSet = new HashSet<>();882    for (int i=0; i<count; i++) {883      URI uri = createUri();884      LocalNode.Builder builder = LocalNode.builder(tracer, bus, uri, uri, registrationSecret);885      for (Capabilities caps: capabilities) {886        builder.add(887          caps,888          new TestSessionFactory((id, hostCaps) -> new HandledSession(uri, hostCaps)));889      }890      Node node = builder.build();891      distributor.add(node);892      nodeSet.add(node);893    }894    return nodeSet;895  }896  @Test897  public void shouldPrioritizeHostsWithTheMostSlotsAvailableForASessionType() {898    // Consider the case where you have 1 Windows machine and 5 linux machines. All of these hosts899    // can run Chrome and Firefox sessions, but only one can run Edge sessions. Ideally, the machine900    // able to run Edge would be sorted last.901    //Create the Distributor902    CombinedHandler handler = new CombinedHandler();903    SessionMap sessions = new LocalSessionMap(tracer, bus);904    handler.addHandler(sessions);905    LocalNewSessionQueue localNewSessionQueue = new LocalNewSessionQueue(906      tracer,907      bus,908      Duration.ofSeconds(2),909      Duration.ofSeconds(2));910    LocalNewSessionQueuer queuer = new LocalNewSessionQueuer(911      tracer,912      bus,913      localNewSessionQueue,914      registrationSecret);915    LocalDistributor distributor = new LocalDistributor(916      tracer,917      bus,918      new PassthroughHttpClient.Factory(handler),919      sessions,920      queuer,921      registrationSecret,922      Duration.ofMinutes(5));923    handler.addHandler(distributor);924    //Create all three Capability types925    Capabilities edge = new ImmutableCapabilities("browserName", "edge");926    Capabilities firefox = new ImmutableCapabilities("browserName", "firefox");927    Capabilities chrome = new ImmutableCapabilities("browserName", "chrome");928    //Store our "expected results" sets for the various browser-specific nodes929    Set<Node> edgeNodes = createNodeSet(distributor, 3, edge, chrome, firefox);930    //chromeNodes is all these new nodes PLUS all the Edge nodes from before931    Set<Node> chromeNodes = createNodeSet(distributor,5, chrome, firefox);932    chromeNodes.addAll(edgeNodes);933    //all nodes support firefox, so add them to the firefoxNodes set934    Set<Node> firefoxNodes = createNodeSet(distributor,3, firefox);935    firefoxNodes.addAll(edgeNodes);936    firefoxNodes.addAll(chromeNodes);937    waitForAllNodesToHaveCapacity(distributor, 11);938    //Assign 5 Chrome and 5 Firefox sessions to the distributor, make sure they don't go to the Edge node939    for (int i=0; i<5; i++) {940      try (NewSessionPayload chromePayload = NewSessionPayload.create(chrome);941           NewSessionPayload firefoxPayload = NewSessionPayload.create(firefox)) {942        Either<SessionNotCreatedException, CreateSessionResponse> chromeResult =943          distributor.newSession(createRequest(chromePayload));944        assertThatEither(chromeResult).isRight();945        Session chromeSession = chromeResult.right().getSession();946        //Ensure the Uri of the Session matches one of the Chrome Nodes, not the Edge Node947        assertThat(948          chromeSession.getUri()).isIn(949          chromeNodes950            .stream().map(Node::getStatus).collect(Collectors.toList())     //List of getStatus() from the Set951            .stream().map(NodeStatus::getUri).collect(Collectors.toList())  //List of getUri() from the Set952        );953        Either<SessionNotCreatedException, CreateSessionResponse> firefoxResult =954          distributor.newSession(createRequest(firefoxPayload));955        assertThatEither(firefoxResult).isRight();956        Session firefoxSession = firefoxResult.right().getSession();957        LOG.info(String.format("Firefox Session %d assigned to %s", i, chromeSession.getUri()));958        boolean inFirefoxNodes = firefoxNodes.stream().anyMatch(node -> node.getUri().equals(firefoxSession.getUri()));959        boolean inChromeNodes = chromeNodes.stream().anyMatch(node -> node.getUri().equals(chromeSession.getUri()));960        //This could be either, or, or both961        assertTrue(inFirefoxNodes || inChromeNodes);962      }963    }964    //The Chrome Nodes should be full at this point, but Firefox isn't... so send an Edge session and make sure it routes to an Edge node965    try (NewSessionPayload edgePayload = NewSessionPayload.create(edge)) {966      Either<SessionNotCreatedException, CreateSessionResponse> edgeResult =967        distributor.newSession(createRequest(edgePayload));968      assertThatEither(edgeResult).isRight();969      Session edgeSession = edgeResult.right().getSession();970      assertTrue(edgeNodes.stream().anyMatch(node -> node.getUri().equals(edgeSession.getUri())));971    }972  }973  private Node createNode(Capabilities stereotype, int count, int currentLoad) {974    URI uri = createUri();975    LocalNode.Builder builder = LocalNode.builder(tracer, bus, uri, uri, registrationSecret);976    for (int i = 0; i < count; i++) {977      builder.add(stereotype, new TestSessionFactory((id, caps) -> new HandledSession(uri, caps)));978    }979    LocalNode node = builder.build();980    for (int i = 0; i < currentLoad; i++) {981      // Ignore the session. We're just creating load.982      node.newSession(new CreateSessionRequest(983        ImmutableSet.copyOf(Dialect.values()),984        stereotype,985        ImmutableMap.of()));986    }987    return node;988  }989  @Test990  @Ignore991  public void shouldCorrectlySetSessionCountsWhenStartedAfterNodeWithSession() {992    fail("write me!");993  }994  @Test995  public void statusShouldIndicateThatDistributorIsNotAvailableIfNodesAreDown()996    throws URISyntaxException {997    Capabilities capabilities = new ImmutableCapabilities("cheese", "peas");998    URI uri = new URI("http://example.com");999    Node node = LocalNode.builder(tracer, bus, uri, uri, registrationSecret)1000      .add(1001        capabilities,1002        new TestSessionFactory((id, caps) -> new Session(id, uri, stereotype, caps, Instant.now())))1003      .advanced()1004      .healthCheck(() -> new HealthCheck.Result(DOWN, "TL;DR"))1005      .build();1006    local.add(node);1007    DistributorStatus status = local.getStatus();1008    assertFalse(status.hasCapacity());1009  }1010  @Test1011  public void disabledNodeShouldNotAcceptNewRequests()1012    throws URISyntaxException1013  {1014    Capabilities capabilities = new ImmutableCapabilities("cheese", "peas");1015    URI uri = new URI("http://example.com");1016    Node node = LocalNode.builder(tracer, bus, uri, uri, registrationSecret)1017      .add(1018        capabilities,1019        new TestSessionFactory((id, caps) -> new Session(id, uri, stereotype, caps, Instant.now())))1020      .advanced()1021      .healthCheck(() -> new HealthCheck.Result(DOWN, "TL;DR"))1022      .build();1023    local.add(node);1024    DistributorStatus status = local.getStatus();1025    assertFalse(status.hasCapacity());1026  }1027  private HttpRequest createRequest(NewSessionPayload payload) {1028    StringBuilder builder = new StringBuilder();1029    try {1030      payload.writeTo(builder);1031    } catch (IOException e) {1032      throw new UncheckedIOException(e);1033    }1034    HttpRequest request = new HttpRequest(POST, "/se/grid/distributor/session");1035    request.setContent(utf8String(builder.toString()));1036    return request;1037  }1038  private URI createUri() {1039    try {1040      return new URI("http://localhost:" + PortProber.findFreePort());1041    } catch (URISyntaxException e) {1042      throw new RuntimeException(e);1043    }1044  }1045  private void waitToHaveCapacity(Distributor distributor) {1046    new FluentWait<>(distributor)1047      .withTimeout(Duration.ofSeconds(5))1048      .pollingEvery(Duration.ofMillis(100))1049      .until(d -> d.getStatus().hasCapacity());1050  }1051  private void waitForAllNodesToHaveCapacity(Distributor distributor, int nodeCount) {1052    try {1053      new FluentWait<>(distributor)1054        .withTimeout(Duration.ofSeconds(5))1055        .pollingEvery(Duration.ofMillis(100))1056        .until(d -> {1057          Set<NodeStatus> nodes = d.getStatus().getNodes();1058          return nodes.size() == nodeCount && nodes.stream().allMatch(1059            node -> node.getAvailability() == UP && node.hasCapacity());1060        });1061    } catch (TimeoutException ex) {1062      Set<NodeStatus> nodes = distributor.getStatus().getNodes();1063      System.out.println("*************");1064      System.out.println("" + nodes.size());1065      nodes.forEach(node -> System.out.println("" + node.hasCapacity()));1066    }1067  }1068  class HandledSession extends Session implements HttpHandler {1069    HandledSession(URI uri, Capabilities caps) {1070      super(new SessionId(UUID.randomUUID()), uri, stereotype, caps, Instant.now());1071    }1072    @Override1073    public HttpResponse execute(HttpRequest req) throws UncheckedIOException {1074      // no-op1075      return new HttpResponse();1076    }1077  }1078}...Source:LocalNewSessionQueueTest.java  
...176                  "value", ImmutableMap.of(177                      "sessionId", sessionId,178                      "capabilities", capabilities)))179              .getBytes(UTF_8));180      queue.complete(reqId, Either.right(sessionResponse));181    }));182    HttpResponse httpResponse = queue.addToQueue(sessionRequest);183    assertThat(isPresent.get()).isTrue();184    assertEquals(httpResponse.getStatus(), HTTP_OK);185  }186  @Test187  public void shouldBeAbleToAddToQueueAndGetErrorResponse() {188    bus.addListener(NewSessionRequestEvent.listener(reqId ->189      queue.complete(reqId, Either.left(new SessionNotCreatedException("Error")))));190    HttpResponse httpResponse = queue.addToQueue(sessionRequest);191    assertEquals(httpResponse.getStatus(), HTTP_INTERNAL_ERROR);192  }193  @Test194  public void shouldBeAbleToRemoveFromQueue() {195    Optional<SessionRequest> httpRequest = queue.remove(new RequestId(UUID.randomUUID()));196    assertFalse(httpRequest.isPresent());197  }198  @Test199  public void shouldBeClearQueue() {200    RequestId requestId = new RequestId(UUID.randomUUID());201    localQueue.injectIntoQueue(sessionRequest);202    int count = queue.clearQueue();203    assertEquals(count, 1);204    assertFalse(queue.remove(requestId).isPresent());205  }206  @Test207  public void shouldBeAbleToGetQueueContents() {208    localQueue.injectIntoQueue(sessionRequest);209    List<Set<Capabilities>> response = queue.getQueueContents()210      .stream()211      .map(SessionRequestCapability::getDesiredCapabilities)212      .collect(Collectors.toList());213    assertThat(response).hasSize(1);214    assertEquals(Set.of(CAPS), response.get(0));215  }216  @Test217  public void shouldBeClearQueueAndFireRejectedEvent() throws InterruptedException {218    AtomicBoolean result = new AtomicBoolean(false);219    RequestId requestId = sessionRequest.getRequestId();220    CountDownLatch latch = new CountDownLatch(1);221    bus.addListener(222        NewSessionRejectedEvent.listener(223            response -> {224              result.set(response.getRequestId().equals(requestId));225              latch.countDown();226            }));227    localQueue.injectIntoQueue(sessionRequest);228    queue.remove(requestId);229    queue.retryAddToQueue(sessionRequest);230    int count = queue.clearQueue();231    assertThat(latch.await(2, SECONDS)).isTrue();232    assertThat(result.get()).isTrue();233    assertEquals(count, 1);234    assertFalse(queue.remove(requestId).isPresent());235  }236  @Test237  public void removingARequestIdThatDoesNotExistInTheQueueShouldNotBeAnError() {238    localQueue.injectIntoQueue(sessionRequest);239    Optional<SessionRequest> httpRequest = queue.remove(new RequestId(UUID.randomUUID()));240    assertFalse(httpRequest.isPresent());241  }242  @Test243  public void shouldBeAbleToAddAgainToQueue() {244    localQueue.injectIntoQueue(sessionRequest);245    Optional<SessionRequest> removed = queue.remove(sessionRequest.getRequestId());246    assertThat(removed).isPresent();247    boolean added = queue.retryAddToQueue(sessionRequest);248    assertTrue(added);249  }250  @Test251  public void shouldBeAbleToRetryRequest() {252    AtomicBoolean isPresent = new AtomicBoolean(false);253    AtomicBoolean retrySuccess = new AtomicBoolean(false);254    AtomicInteger count = new AtomicInteger(0);255    bus.addListener(256        NewSessionRequestEvent.listener(257            reqId -> {258              // Keep a count of event fired259              count.incrementAndGet();260              Optional<SessionRequest> sessionRequest = this.queue.remove(reqId);261              isPresent.set(sessionRequest.isPresent());262              if (count.get() == 1) {263                retrySuccess.set(queue.retryAddToQueue(sessionRequest.get()));264              }265              // Only if it was retried after an interval, the count is 2266              if (count.get() == 2) {267                ImmutableCapabilities capabilities =268                    new ImmutableCapabilities("browserName", "edam");269                try {270                  SessionId sessionId = new SessionId("123");271                  Session session =272                      new Session(273                          sessionId,274                          new URI("http://example.com"),275                          CAPS,276                          capabilities,277                          Instant.now());278                  CreateSessionResponse sessionResponse =279                      new CreateSessionResponse(280                          session,281                          JSON.toJson(282                                  ImmutableMap.of(283                                      "value",284                                      ImmutableMap.of(285                                          "sessionId", sessionId,286                                          "capabilities", capabilities)))287                              .getBytes(UTF_8));288                  queue.complete(reqId, Either.right(sessionResponse));289                } catch (URISyntaxException e) {290                  throw new RuntimeException(e);291                }292              }293            }));294    HttpResponse httpResponse = queue.addToQueue(sessionRequest);295    assertThat(isPresent.get()).isTrue();296    assertThat(retrySuccess.get()).isTrue();297    assertEquals(httpResponse.getStatus(), HTTP_OK);298  }299  @Test(timeout = 5000)300  public void shouldBeAbleToHandleMultipleSessionRequestsAtTheSameTime() {301    bus.addListener(NewSessionRequestEvent.listener(reqId -> {302      queue.remove(reqId);303      ImmutableCapabilities capabilities = new ImmutableCapabilities("browserName", "chrome");304      try {305        SessionId sessionId = new SessionId(UUID.randomUUID());306        Session session =307            new Session(308                sessionId,309                new URI("http://example.com"),310                CAPS,311                capabilities,312                Instant.now());313        CreateSessionResponse sessionResponse = new CreateSessionResponse(314            session,315            JSON.toJson(316                ImmutableMap.of(317                    "value", ImmutableMap.of(318                        "sessionId", sessionId,319                        "capabilities", capabilities)))320                .getBytes(UTF_8));321        queue.complete(reqId, Either.right(sessionResponse));322      } catch (URISyntaxException e) {323        queue.complete(reqId, Either.left(new SessionNotCreatedException(e.getMessage())));324      }325    }));326    ExecutorService executor = Executors.newFixedThreadPool(2);327    Callable<HttpResponse> callable = () -> {328      SessionRequest sessionRequest = new SessionRequest(329        new RequestId(UUID.randomUUID()),330        Instant.now(),331        Set.of(W3C),332        Set.of(CAPS),333        Map.of(),334        Map.of());335      return queue.addToQueue(sessionRequest);336    };337    Future<HttpResponse> firstRequest = executor.submit(callable);...Source:LocalDistributorTest.java  
...380        throw new RuntimeException(e);381      }382    });383    // If the sessions are created serially, then we expect the first384    // session to take up to `delay` ms to complete, followed by the385    // second session.386    assertThat(System.currentTimeMillis() - start).isLessThan(delay * 2);387  }388  private class Handler extends Session implements HttpHandler {389    private Handler(Capabilities capabilities) {390      super(new SessionId(UUID.randomUUID()), uri, new ImmutableCapabilities(), capabilities, Instant.now());391    }392    @Override393    public HttpResponse execute(HttpRequest req) throws UncheckedIOException {394      return new HttpResponse();395    }396  }397}...Source:LocalNewSessionQueue.java  
...58 * <p>59 * The lifecycle of a request can be described as:60 * <ol>61 *   <li>User adds an item on to the queue using {@link #addToQueue(SessionRequest)}. This62 *       will block until the request completes in some way.63 *   <li>After being added, a {@link NewSessionRequestEvent} is fired. Listeners should use64 *       this as an indication to call {@link #remove(RequestId)} to get the session request.65 *   <li>If the session request is completed, then {@link #complete(RequestId, Either)} must66 *       be called. This will not only ensure that {@link #addToQueue(SessionRequest)}67 *       returns, but will also fire a {@link NewSessionRejectedEvent} if the session was68 *       rejected. Positive completions of events are assumed to be notified on the event bus69 *       by other listeners.70 *   <li>If the request cannot be handled right now, call71 *       {@link #retryAddToQueue(SessionRequest)} to return the session request to the front72 *       of the queue.73 * </ol>74 * <p>75 * There is a background thread that will reap {@link SessionRequest}s that have timed out.76 * This means that a request can either complete by a listener calling77 * {@link #complete(RequestId, Either)} directly, or by being reaped by the thread.78 */79@ManagedService(objectName = "org.seleniumhq.grid:type=SessionQueue,name=LocalSessionQueue",80  description = "New session queue")81public class LocalNewSessionQueue extends NewSessionQueue implements Closeable {82  private final EventBus bus;83  private final SlotMatcher slotMatcher;84  private final Duration requestTimeout;85  private final Map<RequestId, Data> requests;86  private final Map<RequestId, TraceContext> contexts;87  private final Deque<SessionRequest> queue;88  private final ReadWriteLock lock = new ReentrantReadWriteLock();89  private final ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(r -> {90    Thread thread = new Thread(r);91    thread.setDaemon(true);92    thread.setName("Local New Session Queue");93    return thread;94  });95  public LocalNewSessionQueue(96    Tracer tracer,97    EventBus bus,98    SlotMatcher slotMatcher,99    Duration retryPeriod,100    Duration requestTimeout,101    Secret registrationSecret) {102    super(tracer, registrationSecret);103    this.slotMatcher = Require.nonNull("Slot matcher", slotMatcher);104    this.bus = Require.nonNull("Event bus", bus);105    Require.nonNull("Retry period", retryPeriod);106    if (retryPeriod.isNegative() || retryPeriod.isZero()) {107      throw new IllegalArgumentException("Retry period must be positive");108    }109    this.requestTimeout = Require.nonNull("Request timeout", requestTimeout);110    if (requestTimeout.isNegative() || requestTimeout.isZero()) {111      throw new IllegalArgumentException("Request timeout must be positive");112    }113    this.requests = new ConcurrentHashMap<>();114    this.queue = new ConcurrentLinkedDeque<>();115    this.contexts = new ConcurrentHashMap<>();116    service.scheduleAtFixedRate(this::timeoutSessions, retryPeriod.toMillis(), retryPeriod.toMillis(), MILLISECONDS);117    new JMXHelper().register(this);118  }119  public static NewSessionQueue create(Config config) {120    LoggingOptions loggingOptions = new LoggingOptions(config);121    Tracer tracer = loggingOptions.getTracer();122    EventBusOptions eventBusOptions = new EventBusOptions(config);123    SessionRequestOptions requestOptions = new SessionRequestOptions(config);124    SecretOptions secretOptions = new SecretOptions(config);125    SlotMatcher slotMatcher = new DistributorOptions(config).getSlotMatcher();126    return new LocalNewSessionQueue(127      tracer,128      eventBusOptions.getEventBus(),129      slotMatcher,130      requestOptions.getSessionRequestRetryInterval(),131      requestOptions.getSessionRequestTimeout(),132      secretOptions.getRegistrationSecret());133  }134  private void timeoutSessions() {135    Instant now = Instant.now();136    Lock readLock = lock.readLock();137    readLock.lock();138    Set<RequestId> ids;139    try {140      ids = requests.entrySet().stream()141        .filter(entry -> isTimedOut(now, entry.getValue()))142        .map(Map.Entry::getKey)143        .collect(Collectors.toSet());144    } finally {145      readLock.unlock();146    }147    Lock writeLock = lock.writeLock();148    try {149      for (RequestId id : ids) {150        failDueToTimeout(id);151      }152    } finally {153      writeLock.unlock();154    }155  }156  private boolean isTimedOut(Instant now, Data data) {157    return data.endTime.isBefore(now);158  }159  @Override160  public HttpResponse addToQueue(SessionRequest request) {161    Require.nonNull("New session request", request);162    Require.nonNull("Request id", request.getRequestId());163    TraceContext context = TraceSessionRequest.extract(tracer, request);164    try (Span span = context.createSpan("sessionqueue.add_to_queue")) {165      contexts.put(request.getRequestId(), context);166      Data data = injectIntoQueue(request);167      if (isTimedOut(Instant.now(), data)) {168        failDueToTimeout(request.getRequestId());169      }170      Either<SessionNotCreatedException, CreateSessionResponse> result;171      try {172        if (data.latch.await(requestTimeout.toMillis(), MILLISECONDS)) {173          result = data.result;174        } else {175          result = Either.left(new SessionNotCreatedException("New session request timed out"));176        }177      } catch (InterruptedException e) {178        Thread.currentThread().interrupt();179        result = Either.left(new SessionNotCreatedException("Interrupted when creating the session", e));180      } catch (RuntimeException e) {181        result = Either.left(new SessionNotCreatedException("An error occurred creating the session", e));182      }183      Lock writeLock = this.lock.writeLock();184      writeLock.lock();185      try {186        requests.remove(request.getRequestId());187        queue.remove(request);188      } finally {189        writeLock.unlock();190      }191      HttpResponse res = new HttpResponse();192      if (result.isRight()) {193        res.setContent(Contents.bytes(result.right().getDownstreamEncodedResponse()));194      } else {195        res.setStatus(HTTP_INTERNAL_ERROR)196          .setContent(Contents.asJson(Collections.singletonMap("value", result.left())));197      }198      return res;199    }200  }201  @VisibleForTesting202  Data injectIntoQueue(SessionRequest request) {203    Require.nonNull("Session request", request);204    Data data = new Data(request.getEnqueued());205    Lock writeLock = lock.writeLock();206    writeLock.lock();207    try {208      requests.put(request.getRequestId(), data);209      queue.addLast(request);210    } finally {211      writeLock.unlock();212    }213    bus.fire(new NewSessionRequestEvent(request.getRequestId()));214    return data;215  }216  @Override217  public boolean retryAddToQueue(SessionRequest request) {218    Require.nonNull("New session request", request);219    boolean added;220    TraceContext context = contexts.getOrDefault(request.getRequestId(), tracer.getCurrentContext());221    try (Span span = context.createSpan("sessionqueue.retry")) {222      Lock writeLock = lock.writeLock();223      writeLock.lock();224      try {225        if (!requests.containsKey(request.getRequestId())) {226          return false;227        }228        if (queue.contains(request)) {229          // No need to re-add this230          return true;231        } else {232          added = queue.offerFirst(request);233        }234      } finally {235        writeLock.unlock();236      }237      if (added) {238        bus.fire(new NewSessionRequestEvent(request.getRequestId()));239      }240      return added;241    }242  }243  @Override244  public Optional<SessionRequest> remove(RequestId reqId) {245    Require.nonNull("Request ID", reqId);246    Lock writeLock = lock.writeLock();247    writeLock.lock();248    try {249      Iterator<SessionRequest> iterator = queue.iterator();250      while (iterator.hasNext()) {251        SessionRequest req = iterator.next();252        if (reqId.equals(req.getRequestId())) {253          iterator.remove();254          return Optional.of(req);255        }256      }257      return Optional.empty();258    } finally {259      writeLock.unlock();260    }261  }262  @Override263  public Optional<SessionRequest> getNextAvailable(Set<Capabilities> stereotypes) {264    Require.nonNull("Stereotypes", stereotypes);265    Predicate<Capabilities> matchesStereotype =266      caps -> stereotypes.stream().anyMatch(stereotype -> slotMatcher.matches(stereotype, caps));267    Lock writeLock = lock.writeLock();268    writeLock.lock();269    try {270      Optional<SessionRequest> maybeRequest =271          queue.stream()272              .filter(req -> req.getDesiredCapabilities().stream().anyMatch(matchesStereotype))273              .findFirst();274      maybeRequest.ifPresent(req -> {275        this.remove(req.getRequestId());276      });277      return maybeRequest;278    } finally {279      writeLock.unlock();280    }281  }282  @Override283  public void complete(RequestId reqId, Either<SessionNotCreatedException, CreateSessionResponse> result) {284    Require.nonNull("New session request", reqId);285    Require.nonNull("Result", result);286    TraceContext context = contexts.getOrDefault(reqId, tracer.getCurrentContext());287    try (Span span = context.createSpan("sessionqueue.completed")) {288      Lock readLock = lock.readLock();289      readLock.lock();290      Data data;291      try {292        data = requests.get(reqId);293      } finally {294        readLock.unlock();295      }296      if (data == null) {297        return;298      }299      Lock writeLock = lock.writeLock();300      writeLock.lock();301      try {302        requests.remove(reqId);303        queue.removeIf(req -> reqId.equals(req.getRequestId()));304        contexts.remove(reqId);305      } finally {306        writeLock.unlock();307      }308      if (result.isLeft()) {309        bus.fire(new NewSessionRejectedEvent(new NewSessionErrorResponse(reqId, result.left().getMessage())));310      }311      data.setResult(result);312    }313  }314  @Override315  public int clearQueue() {316    Lock writeLock = lock.writeLock();317    writeLock.lock();318    try {319      int size = queue.size();320      queue.clear();321      requests.forEach((reqId, data) -> {322        data.setResult(Either.left(new SessionNotCreatedException("Request queue was cleared")));323        bus.fire(new NewSessionRejectedEvent(324          new NewSessionErrorResponse(reqId, "New session queue was forcibly cleared")));325      });326      requests.clear();327      return size;328    } finally {329      writeLock.unlock();330    }331  }332  @Override333  public List<SessionRequestCapability> getQueueContents() {334    Lock readLock = lock.readLock();335    readLock.lock();336    try {337      return queue.stream()338        .map(req ->339          new SessionRequestCapability(req.getRequestId(), req.getDesiredCapabilities()))340        .collect(Collectors.toList());341    } finally {342      readLock.unlock();343    }344  }345  @ManagedAttribute(name = "NewSessionQueueSize")346  public int getQueueSize() {347    return queue.size();348  }349  @Override350  public boolean isReady() {351    return true;352  }353  @Override354  public void close() throws IOException {355    service.shutdownNow();356  }357  private void failDueToTimeout(RequestId reqId) {358    complete(reqId, Either.left(new SessionNotCreatedException("Timed out creating session")));359  }360  private class Data {361    public final Instant endTime;362    public Either<SessionNotCreatedException, CreateSessionResponse> result;363    private boolean complete;364    private CountDownLatch latch = new CountDownLatch(1);365    public Data(Instant enqueued) {366      this.endTime = enqueued.plus(requestTimeout);367      this.result = Either.left(new SessionNotCreatedException("Session not created"));368    }369    public synchronized void setResult(Either<SessionNotCreatedException, CreateSessionResponse> result) {370      if (complete) {371        return;372      }373      this.result = result;374      complete = true;375      latch.countDown();376    }377  }378}...complete
Using AI Code Generation
1package org.openqa.selenium.grid.sessionqueue.local;2import org.openqa.selenium.grid.config.Config;3import org.openqa.selenium.grid.data.Session;4import org.openqa.selenium.grid.sessionqueue.NewSessionQueue;5import org.openqa.selenium.internal.Require;6import org.openqa.selenium.remote.tracing.Tracer;7import java.net.URI;8import java.util.Objects;9import java.util.Optional;10import java.util.concurrent.ConcurrentLinkedQueue;11import java.util.concurrent.atomic.AtomicInteger;12public class LocalNewSessionQueue implements NewSessionQueue {13  private final Tracer tracer;14  private final ConcurrentLinkedQueue<Session> queue;15  private final AtomicInteger size;16  public LocalNewSessionQueue(Tracer tracer, int size) {17    this.tracer = Require.nonNull("Tracer", tracer);18    this.size = new AtomicInteger(size);19    this.queue = new ConcurrentLinkedQueue<>();20  }21  public static NewSessionQueue create(Config config) {22    return new LocalNewSessionQueue(23      config.getTracer(),24      config.getInt("queue-size").orElse(10));25  }26  public void add(Session session) {27    Require.nonNull("Session", session);28    if (size.decrementAndGet() >= 0) {29      queue.add(session);30    } else {31      size.incrementAndGet();32    }33  }34  public Optional<Session> remove(URI uri) {35    Require.nonNull("URI", uri);36    return Optional.ofNullable(queue.poll());37  }38  public void complete(URI uri) {39    Require.nonNull("URI", uri);40    size.incrementAndGet();41  }42  public boolean equals(Object o) {43    if (!(o instanceof LocalNewSessionQueue)) {44      return false;45    }46    LocalNewSessionQueue that = (LocalNewSessionQueue) o;47    return Objects.equals(this.tracer, that.tracer) &&48           Objects.equals(this.size, that.size) &&49           Objects.equals(this.queue, that.queue);50  }51  public int hashCode() {52    return Objects.hash(tracer, queue, size);53  }54}55package org.openqa.selenium.grid.sessionqueue.local;56import org.openqa.selenium.grid.config.Config;57import org.openqa.selenium.grid.data.Session;58import org.openqa.selenium.grid.sessionqueue.NewSessionQueue;59import org.openqa.selenium.internal.Require;60import org.openqa.selenium.remote.tracing.Tracer;61import javacomplete
Using AI Code Generation
1import org.openqa.selenium.grid.sessionqueue.local.LocalNewSessionQueue;2import org.openqa.selenium.grid.sessionqueue.NewSessionQueue;3import org.openqa.selenium.remote.tracing.Tracer;4import org.openqa.selenium.remote.tracing.DistributedTracer;5public class LocalNewSessionQueueExample {6    public static void main(String[] args) {7        Tracer tracer = new DistributedTracer();8        NewSessionQueue sessionQueue = new LocalNewSessionQueue(tracer);9        sessionQueue.complete(sessionId);10    }11}complete
Using AI Code Generation
1package com.selenium4beginners.java.webdriver;2import java.net.URI;3import java.net.URISyntaxException;4import java.util.ArrayList;5import java.util.List;6import org.openqa.selenium.Capabilities;7import org.openqa.selenium.ImmutableCapabilities;8import org.openqa.selenium.grid.config.MapConfig;9import org.openqa.selenium.grid.data.Session;10import org.openqa.selenium.grid.data.SessionId;11import org.openqa.selenium.grid.sessionqueue.local.LocalNewSessionQueue;12import org.openqa.selenium.remote.NewSessionPayload;13import org.openqa.selenium.remote.SessionIdGenerator;14import org.openqa.selenium.remote.SessionIdGenerator.SauceSessionIdGenerator;15import org.openqa.selenium.remote.http.HttpClient;16import org.openqa.selenium.remote.http.HttpMethod;17import org.openqa.selenium.remote.http.HttpResponse;18import org.openqa.selenium.remote.http.HttpResponseCode;19import com.google.common.collect.ImmutableMap;20public class CompleteLocalNewSessionQueue {21	public static void main(String[] args) throws URISyntaxException {22		NewSessionPayload payload = new NewSessionPayload(23				new ImmutableCapabilities("browserName", "chrome"));24		SessionIdGenerator generator = new SauceSessionIdGenerator();25		SessionId id = generator.generateSessionId();26				new ImmutableCapabilities("browserName", "chrome"));27		LocalNewSessionQueue queue = new LocalNewSessionQueue(28				new MapConfig(ImmutableMap.of("session-queue-timeout", "30")));29		queue.add(payload);30		Session nextSession = queue.next();31		int size = queue.size();32		List<Capabilities> capabilities = queue.getCapabilities();33		List<Capabilities> capabilitiesList = queue.getCapabilitiesList();34		String capabilitiesString = queue.getCapabilitiesString();complete
Using AI Code Generation
1import org.openqa.selenium.grid.sessionqueue.local.LocalNewSessionQueue;2import org.openqa.selenium.grid.sessionqueue.NewSessionQueue;3import org.openqa.selenium.grid.sessionqueue.local.LocalNewSessionQueue.CompleteResponse;4import org.openqa.selenium.internal.Require;5import org.openqa.selenium.remote.http.HttpRequest;6import org.openqa.selenium.remote.http.HttpResponse;7import org.openqa.selenium.remote.tracing.Tracer;8import java.util.Objects;9import java.util.UUID;10import java.util.concurrent.TimeUnit;11public class CompleteResponse extends HttpResponse {12  private final NewSessionQueue queue;13  private final HttpRequest req;14  private final UUID id;15  private final Tracer tracer;16  public CompleteResponse(NewSessionQueue queue, HttpRequest req, UUID id, Tracer tracer) {17    super(200);18    this.queue = Require.nonNull("Queue", queue);19    this.req = Require.nonNull("HTTP request", req);20    this.id = Require.nonNull("Session ID", id);21    this.tracer = Require.nonNull("Tracer", tracer);22  }23  public void writeTo(OutputStream output) throws IOException {24    try (Span span = tracer.getCurrentContext().createSpan("new_session_queue.complete")) {25      span.addTag("session.id", id.toString());26      try {27        queue.complete(id, TimeUnit.SECONDS.toMillis(10));28      } catch (TimeoutException e) {29        span.addTag("error", true);30        span.addTag("message", e.getMessage());31        span.addTag("stacktrace", Throwables.getStackTraceAsString(e));32      }33    }34  }35}36public class LocalNewSessionQueue implements NewSessionQueue {37  public CompleteResponse complete(UUID id, long timeout) {38    Objects.requireNonNull(id, "Session ID to complete must be set.");39    try (Span span = tracer.getCurrentContext().createSpan("new_session_queue.complete")) {40      span.addTag("session.id", id.toString());41      span.addTag("timeout", timeout);42      SessionRequest request = requests.remove(id);43      if (request == null) {44        span.addTag("error", true);45        span.addTag("message", "No such request");46        throw new NoSuchElementException("No such request: " + id);47      }48      request.complete();49      return new CompleteResponse(this, req, id, tracer);50    }51  }52}53public class LocalNewSessionQueue implements NewSessionQueue {54  public static class CompleteResponse extends HttpResponse {55    private final NewSessionQueue queue;56    private final HttpRequest req;complete
Using AI Code Generation
1import org.openqa.selenium.grid.config.Config;2import org.openqa.selenium.grid.config.MapConfig;3import org.openqa.selenium.grid.data.Session;4import org.openqa.selenium.grid.node.local.LocalNode;5import org.openqa.selenium.grid.sessionqueue.local.LocalNewSessionQueue;6import org.openqa.selenium.remote.http.HttpClient;7import org.openqa.selenium.remote.tracing.Tracer;8import org.openqa.selenium.remote.tracing.zipkin.ZipkinTracer;9import org.openqa.selenium.remote.tracing.zipkin.ZipkinTracerFactory;10import org.openqa.selenium.remote.tracing.zipkin.ZipkinTracerOptions;11import java.net.URI;12import java.util.concurrent.ExecutionException;13public class LocalNodeTest {14    public static void main(String[] args) throws ExecutionException, InterruptedException {15        Tracer tracer = new ZipkinTracer(new ZipkinTracerFactory(), new ZipkinTracerOptions());16        Config config = new MapConfig();17        LocalNode node = new LocalNode(18                new LocalNewSessionQueue(),19                HttpClient.Factory.createDefault(),20                HttpClient.Factory.createDefault(),21        Session session = node.newSession(null).get();22        node.complete(session.getId());23    }24}252021-02-24 15:59:04.229:INFO:oejs.Server:main: jetty-9.4.33.v20201020; built: 2020-10-20T22:32:19.116Z; git: 6f1b6c8b6d9b6b1e6b7c6e8a0a7f2d2f1d1a1a66; jvm 1.8.0_275-b01complete
Using AI Code Generation
1package com.selenium.grid;2import java.io.IOException;3import java.net.URI;4import java.net.URISyntaxException;5import java.util.Optional;6import java.util.concurrent.TimeUnit;7import java.util.logging.Level;8import java.util.logging.Logger;9import org.openqa.selenium.Capabilities;10import org.openqa.selenium.ImmutableCapabilities;11import org.openqa.selenium.SessionNotCreatedException;12import org.openqa.selenium.grid.data.NewSessionRequest;13import org.openqa.selenium.grid.data.NewSessionResponse;14import org.openqa.selenium.grid.data.Session;15import org.openqa.selenium.grid.distributor.local.LocalDistributor;16import org.openqa.selenium.grid.node.local.LocalNode;17import org.openqa.selenium.grid.sessionqueue.local.LocalNewSessionQueue;18import org.openqa.selenium.grid.web.Values;19import org.openqa.selenium.internal.Require;20import org.openqa.selenium.json.Json;21import org.openqa.selenium.remote.http.HttpClient;22import org.openqa.selenium.remote.tracing.DefaultTestTracer;23import org.openqa.selenium.remote.tracing.Tracer;24import com.google.common.collect.ImmutableMap;25public class LocalDistributorSessionQueue {26  private static final Logger LOG = Logger.getLogger(LocalDistributorSessionQueue.class.getName());27  public static void main(String[] args) throws URISyntaxException {28    Tracer tracer = DefaultTestTracer.createTracer();29    HttpClient.Factory clientFactory = HttpClient.Factory.createDefault();30    LocalDistributor distributor = new LocalDistributor(31        new LocalNewSessionQueue(32            TimeUnit.SECONDS));33    LocalNode node = new LocalNode(34        new ImmutableCapabilities("browserName", "chrome"));35    node.start();36    NewSessionRequest request = new NewSessionRequest(37        new ImmutableCapabilities("browserName", "chrome"),38        new ImmutableCapabilities());39    try {40      NewSessionResponse response = distributor.newSession(request);41      Session session = response.getSession();42      System.out.println("Session ID: " + session.getId());43      System.out.println("List of all the sessions waiting in the queue: " + distributor.getNewSessionQueue().complete());44      System.out.println("Session at the top of theLambdaTest’s Selenium 4 tutorial is covering every aspects of Selenium 4 testing with examples and best practices. Here you will learn basics, such as how to upgrade from Selenium 3 to Selenium 4, to some advanced concepts, such as Relative locators and Selenium Grid 4 for Distributed testing. Also will learn new features of Selenium 4, such as capturing screenshots of specific elements, opening a new tab or window on the browser, and new protocol adoptions.
Upgrading From Selenium 3 To Selenium 4?: In this chapter, learn in detail how to update Selenium 3 to Selenium 4 for Java binding. Also, learn how to upgrade while using different build tools such as Maven or Gradle and get comprehensive guidance for upgrading Selenium.
What’s New In Selenium 4 & What’s Being Deprecated? : Get all information about new implementations in Selenium 4, such as W3S protocol adaption, Optimized Selenium Grid, and Enhanced Selenium IDE. Also, learn what is deprecated for Selenium 4, such as DesiredCapabilites and FindsBy methods, etc.
Selenium 4 With Python: Selenium supports all major languages, such as Python, C#, Ruby, and JavaScript. In this chapter, learn how to install Selenium 4 for Python and the features of Python in Selenium 4, such as Relative locators, Browser manipulation, and Chrom DevTool protocol.
Selenium 4 Is Now W3C Compliant: JSON Wireframe protocol is retiring from Selenium 4, and they are adopting W3C protocol to learn in detail about the advantages and impact of these changes.
How To Use Selenium 4 Relative Locator? : Selenium 4 came with new features such as Relative Locators that allow constructing locators with reference and easily located constructors nearby. Get to know its different use cases with examples.
Selenium Grid 4 Tutorial For Distributed Testing: Selenium Grid 4 allows you to perform tests over different browsers, OS, and device combinations. It also enables parallel execution browser testing, reads up on various features of Selenium Grid 4 and how to download it, and runs a test on Selenium Grid 4 with best practices.
Selenium Video Tutorials: Binge on video tutorials on Selenium by industry experts to get step-by-step direction from automating basic to complex test scenarios with Selenium.
LambdaTest also provides certification for Selenium testing to accelerate your career in Selenium automation testing.
Get 100 minutes of automation test minutes FREE!!
