Path: blob/trunk/dotnet/src/support/UI/SlowLoadableComponent{T}.cs
2885 views
// <copyright file="SlowLoadableComponent{T}.cs" company="Selenium Committers"> // Licensed to the Software Freedom Conservancy (SFC) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The SFC licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // </copyright> using System; using System.Globalization; using System.Threading; namespace OpenQA.Selenium.Support.UI; /// <summary> /// <para>A <see cref="LoadableComponent{T}"/> which might not have finished loading when Load() returns.</para> /// <para>After a call to Load(), the IsLoaded property should continue to return false until the component has fully loaded.</para> /// <para> /// <code> /// // Example usage: /// new MySlowComponent().Load(); /// </code> /// </para> /// </summary> /// <remarks>Override the HandleErrors() method to check for error conditions which caused <see cref="Load()"/> to fail.</remarks> /// <typeparam name="T">The type to be returned (normally the subclass' type)</typeparam> public abstract class SlowLoadableComponent<T> : LoadableComponent<T> where T : SlowLoadableComponent<T> { /// <summary> /// Initializes a new instance of the <see cref="SlowLoadableComponent{T}"/> class. /// </summary> /// <param name="timeout">The <see cref="TimeSpan"/> within which the component should be loaded.</param> protected SlowLoadableComponent(TimeSpan timeout) : this(timeout, SystemClock.Instance) { } /// <summary> /// Initializes a new instance of the <see cref="SlowLoadableComponent{T}"/> class. /// </summary> /// <param name="timeout">The <see cref="TimeSpan"/> within which the component should be loaded.</param> /// <param name="clock">The <see cref="IClock"/> to use when measuring the timeout.</param> /// <exception cref="ArgumentNullException">If <paramref name="clock"/> is <see langword="null"/>.</exception> protected SlowLoadableComponent(TimeSpan timeout, IClock clock) { this.Clock = clock ?? throw new ArgumentNullException(nameof(clock)); this.Timeout = timeout; } /// <summary> /// Gets or sets the time to sleep between each check of the load status of the component. /// </summary> public TimeSpan SleepInterval { get; set; } = TimeSpan.FromMilliseconds(200); /// <summary> /// Gets the timeout interval before which this component must be considered loaded. /// </summary> protected TimeSpan Timeout { get; } /// <summary> /// Gets the clock object providing timing for monitoring the load status of this component. /// </summary> protected IClock Clock { get; } /// <summary> /// Ensures that the component is currently loaded. /// </summary> /// <returns>The loaded component.</returns> /// <remarks>This is equivalent to the Get() method in Java version.</remarks> public override T Load() { if (this.IsLoaded) { return (T)this; } else { this.TryLoad(); } DateTime end = this.Clock.LaterBy(this.Timeout); while (this.Clock.IsNowBefore(end)) { if (this.IsLoaded) { return (T)this; } this.HandleErrors(); this.Wait(); } if (this.IsLoaded) { return (T)this; } else { if (string.IsNullOrEmpty(UnableToLoadMessage)) { this.UnableToLoadMessage = string.Format(CultureInfo.InvariantCulture, "Timed out after {0} seconds.", this.Timeout.TotalSeconds); } throw new WebDriverTimeoutException(this.UnableToLoadMessage); } } /// <summary> /// Checks for well known error cases, which would mean that loading has finished, but an error /// condition was seen. /// </summary> /// <remarks> /// This method should be overridden so that expected errors can be automatically handled. /// </remarks> protected virtual void HandleErrors() { // no-op by default } /// <summary> /// Waits between polls of the load status of this component. /// </summary> protected virtual void Wait() { Thread.Sleep(this.SleepInterval); } }