Basics

Getting the result out of a Try

While trivial with Scala’s pattern matching it is less obvious from Java.

Scala:

import com.twitter.util.{Return, Throw, Try}

def extract(t: Try[String]): String =
  t match {
    case Return(ret) => ret
    case Throw(ex) => ex.toString
  }

Java:

import com.twitter.util.Return;
import com.twitter.util.Throw;
import com.twitter.util.Try;

String extract(Try<String> t) {
  if (t.isReturn()) {
    return t.get();
  } else {
    return t.throwable().toString();
  }
}

Conversions between Twitter’s Try and Scala’s Try

Scala’s scala.util.Try, scala.util.Success, and scala.util.Failure were based on Twitter’s com.twitter.util.Try, com.twitter.util.Return and com.twitter.util.Throw and converting to and from them is sometimes necessary.

Scala:

import com.twitter.util.{Try => TwitterTry}

def toScalaTry(twitterTry: TwitterTry[String]): scala.util.Try[String] =
  twitterTry.asScala

def fromScalaTry(scalaTry: scala.util.Try[String]): TwitterTry[String] =
  TwitterTry.fromScala(scalaTry)

Java:

import com.twitter.util.Try;

scala.util.Try<String> toScalaTry(Try<String> twitterTry) {
  return twitterTry.asScala();
}

Try<String> fromScalaTry(scala.util.Try<String> scalaTry) {
  return Try.fromScala(scalaTry);
}

Controlling Time for tests

The current time, com.twitter.util.Time.now, can be manipulated for writing deterministic tests with Time.withTimeAt, Time.withCurrentTimeFrozen, Time.withTimeFunction, and Time.sleep.

While Time.now is not a “global”, it is properly propagated through to other code via the standard usage of Locals throughout util. Specifically, code using Futures, FuturePools, and MockTimers will see the manipulated Time.now.

Scala:

import com.twitter.conversions.DurationOps._
import com.twitter.util.{FuturePool, Time}

val time = Time.fromMilliseconds(123456L)
Time.withTimeAt(time) { timeControl =>
  assert(Time.now == time)

  // you can control time via the `TimeControl` instance.
  timeControl.advance(2.seconds)
  FuturePool.unboundedPool {
    assert(Time.now == time + 2.seconds)
  }
}

Java:

import com.twitter.util.Duration;
import com.twitter.util.FuturePool;
import com.twitter.util.Time;
import static com.twitter.util.Function.func;
import static com.twitter.util.Function.func0;

Time time = Time.fromMilliseconds(123456L);
Time.withTimeAt(time, func(timeControl -> {
  assert(Time.now().equals(time));

  // you can control time via the `TimeControl` instance.
  timeControl.advance(Duration.fromSeconds(2));
  FuturePools.unboundedPool().apply(func0(() -> {
    assert(Time.now().equals(time.plus(Duration.fromSeconds(2))));
    return BoxedUnit.UNIT;
  }));
  return null;
}));

Controlling Timers

For the same reasons you would want to control Time deterministically, you may have code that relies on a Timer and need the same abilities. Enter com.twitter.util.MockTimer, which plays well with the Time manipulation methods discussed above. It adds a method to Timer, tick(), which runs all TimerTasks that are past their deadline.

Scala:

import com.twitter.conversions.DurationOps._
import com.twitter.util.{Future, MockTimer, Time}

Time.withCurrentTimeFrozen { timeControl =>
  val timer = new MockTimer()
  // schedule some work for later
  val f: Future[String] = timer.doLater(1.millisecond) {
    // some work, and return a status string
    "done"
  }
  // the task will not execute until we say so.
  assert(!f.isDefined)

  // move time forward, past when the task should be done
  timeControl.advance(2.milliseconds)
  // the task will not execute until we `Timer.tick()`
  assert(!f.isDefined)

  // let the Timer run and the task will run
  timer.tick()
  assert(f.isDefined)
}
import com.twitter.util.Duration;
import com.twitter.util.Future;
import com.twitter.util.MockTimer;
import com.twitter.util.Time;
import static com.twitter.util.Function.func;
import static com.twitter.util.Function.func0;

Time.withCurrentTimeFrozen(func(timeControl -> {
  MockTimer timer = new MockTimer();
  Future<String> f = timer.doLater(Duration.fromMilliseconds(1),
    func0(() -> "done"));
  // the task will not execute until we say so.
  assert(!f.isDefined());

  // move time forward, past when the task should be done
  timeControl.advance(Duration.fromMilliseconds(2));
  // the task will not execute until we `Timer.tick()`
  assert(!f.isDefined());

  // let the Timer run and the task will run
  timer.tick();
  assert(!f.isDefined());
  return null;
}));