/* * Copyright 2017 the original author or authors. * * Licensed 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. */ package org.gradle.internal.operations; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.reflect.TypeToken; import org.gradle.api.Action; import org.gradle.api.Nullable; import org.gradle.internal.Cast; import org.gradle.internal.UncheckedException; import org.gradle.internal.logging.events.OperationIdentifier; import org.gradle.internal.progress.BuildOperationDescriptor; import org.gradle.internal.progress.BuildOperationState; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; /** * A BuildOperationExecutor for tests. * Simply execute given operations, does not support current/parent operations. */ public class TestBuildOperationExecutor implements BuildOperationExecutor { public final BuildOperationLog log = new BuildOperationLog(); @Override public BuildOperationState getCurrentOperation() { return new BuildOperationState() { @Override public Object getId() { return new OperationIdentifier(1L); } @Override public Object getParentId() { return null; } }; } public List<BuildOperationDescriptor> getOperations() { return log.getDescriptors(); } @Override public void run(RunnableBuildOperation buildOperation) { log.run(buildOperation); } @Override public <T> T call(CallableBuildOperation<T> buildOperation) { return log.call(buildOperation); } @Override public <O extends RunnableBuildOperation> void runAll(Action<BuildOperationQueue<O>> generator) { generator.execute(new TestBuildOperationQueue<O>(log)); } @Override public <O extends BuildOperation> void runAll(BuildOperationWorker<O> worker, Action<BuildOperationQueue<O>> schedulingAction) { throw new UnsupportedOperationException(); } private static class TestBuildOperationContext implements BuildOperationContext { private Object result; @Override public void failed(@Nullable Throwable failure) { } @Override public void setResult(@Nullable Object result) { this.result = result; } } public static class TestBuildOperationQueue<O extends RunnableBuildOperation> implements BuildOperationQueue<O> { public final BuildOperationLog log; public TestBuildOperationQueue() { this(new BuildOperationLog()); } private TestBuildOperationQueue(BuildOperationLog log) { this.log = log; } @Override public void add(O operation) { log.run(operation); } @Override public void cancel() { throw new UnsupportedOperationException(); } @Override public void waitForCompletion() throws MultipleBuildOperationFailures { throw new UnsupportedOperationException(); } @Override public void setLogLocation(String logLocation) { throw new UnsupportedOperationException(); } } public static class BuildOperationLog { private final List<Record> records = new CopyOnWriteArrayList<Record>(); public List<BuildOperationDescriptor> getDescriptors() { return Lists.transform(new ArrayList<Record>(records), new Function<Record, BuildOperationDescriptor>() { @Override public BuildOperationDescriptor apply(@javax.annotation.Nullable Record input) { return input.descriptor; } }); } private <D, R, T extends BuildOperationType<D, R>> Record mostRecent(Class<T> type) { Class<D> detailsType = extractDetailsType(type); ImmutableList<Record> copy = ImmutableList.copyOf(this.records).reverse(); for (Record record : copy) { Object details = record.descriptor.getDetails(); if (detailsType.isInstance(details)) { return record; } } throw new AssertionError("Did not find operation with details of type: " + detailsType.getName()); } public <R, D, T extends BuildOperationType<D, R>> D mostRecentDetails(Class<T> type) { return extractDetailsType(type).cast(mostRecent(type).descriptor.getDetails()); } public <R, D, T extends BuildOperationType<D, R>> R mostRecentResult(Class<T> type) { Record record = mostRecent(type); Object result = record.result; Class<R> resultType = extractResultType(type); if (resultType.isInstance(result)) { return resultType.cast(result); } else { throw new AssertionError("Expected result type " + resultType.getName() + ", got " + result.getClass().getName()); } } public <D, R, T extends BuildOperationType<D, R>> Throwable mostRecentFailure(Class<T> type) { return mostRecent(type).failure; } private static <D, R, T extends BuildOperationType<D, R>> Class<R> extractResultType(Class<T> type) { return Cast.uncheckedCast(new TypeToken<R>(type) { }.getRawType()); } private static <D, T extends BuildOperationType<D, ?>> Class<D> extractDetailsType(Class<T> type) { return Cast.uncheckedCast(new TypeToken<D>(type) { }.getRawType()); } private static class Record { public final BuildOperationDescriptor descriptor; public Object result; public Throwable failure; private Record(BuildOperationDescriptor descriptor) { this.descriptor = descriptor; } } private void run(RunnableBuildOperation buildOperation) { Record record = new Record(buildOperation.description().build()); records.add(record); TestBuildOperationContext context = new TestBuildOperationContext(); try { buildOperation.run(context); } catch (Throwable failure) { record.failure = failure; throw UncheckedException.throwAsUncheckedException(failure); } record.result = context.result; } private <T> T call(CallableBuildOperation<T> buildOperation) { Record record = new Record(buildOperation.description().build()); records.add(record); TestBuildOperationContext context = new TestBuildOperationContext(); T t; try { t = buildOperation.call(context); } catch (Throwable failure) { record.failure = failure; throw UncheckedException.throwAsUncheckedException(failure); } record.result = context.result; return t; } } }