Skip to content

Commit

Permalink
Merge pull request #300 from eclipse-passage/565011
Browse files Browse the repository at this point in the history
Bug 565011 rebuild the core of condition expression evaluation
  • Loading branch information
eparovyshnaya authored Jul 9, 2020
2 parents d9ec680 + 8b73cd4 commit c2706be
Show file tree
Hide file tree
Showing 7 changed files with 298 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public final class BasePermissionEmittingService implements PermissionEmittingSe
private final StringServiceId id = new StringServiceId("default-emitter"); //$NON-NLS-1$
private final ExpressionPasringRegistry parsers;
private final ExpressionTokenAssessorsRegistry assessors;
private ExpressionEvaluatorsRegistry evaluators;
private final ExpressionEvaluatorsRegistry evaluators;

public BasePermissionEmittingService(//
ExpressionPasringRegistry parsers, //
Expand Down Expand Up @@ -109,15 +109,15 @@ private Emission emitFor(Condition condition, LicensedProduct product) {
private void expressionIsSatisfied(Condition condition)
throws ExpressionParsingException, ExpressionEvaluationException, LicensingException {
ExpressionTokenAssessmentService assessor = //
evaluator(condition.evaluationInstructions().type());
assessor(condition.evaluationInstructions().type());
ParsedExpression expression = new FormalizedExpression( //
condition.evaluationInstructions().expression(), //
parsers.get()).get();
ExpressionEvaluationService evaluator = evaluators.get().service(expression.protocol());
evaluator.evaluate(expression, assessor);
}

private ExpressionTokenAssessmentService evaluator(EvaluationType type) throws LicensingException {
private ExpressionTokenAssessmentService assessor(EvaluationType type) throws LicensingException {
if (!assessors.get().hasService(type)) {
throw new LicensingException(String.format(
"Expression of [%s] evaluation type cannot be asessed: no evaluation services are geristered for the type", //$NON-NLS-1$ FIXME
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

import java.util.Optional;

import org.eclipse.passage.lic.api.tests.fakes.conditions.evaluation.FakeConditionExpressionEvaluator;
import org.eclipse.passage.lic.api.tests.fakes.conditions.evaluation.FakeExpressionEvaluationService;
import org.eclipse.passage.lic.api.tests.fakes.conditions.evaluation.FakeConditionExpressionParser;
import org.eclipse.passage.lic.api.tests.fakes.conditions.evaluation.FakeExpressionTokenAssessmentService;
import org.eclipse.passage.lic.api.tests.fakes.conditions.mining.FakeMinedConditions;
Expand Down Expand Up @@ -182,7 +182,7 @@ public final void prohibitsExpressionEvaluationServicesExtension() {
@Test
public final void prohibitsInjectionIntoExpressionEvaluationServices() {
assertServiceInjectionsIsProhibited(config().expressionEvaluators().get(),
new FakeConditionExpressionEvaluator());
new FakeExpressionEvaluationService());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
*******************************************************************************/
package org.eclipse.passage.lic.api.tests.fakes.conditions;

import java.util.Optional;

import org.eclipse.passage.lic.internal.api.conditions.Condition;
import org.eclipse.passage.lic.internal.api.conditions.EvaluationInstructions;
import org.eclipse.passage.lic.internal.api.conditions.ValidityPeriod;
Expand All @@ -20,24 +22,56 @@
@SuppressWarnings("restriction")
public final class FakeCondition implements Condition {

private Optional<VersionMatch> version = Optional.empty();
private Optional<ValidityPeriod> period = Optional.empty();
private Optional<String> feature = Optional.empty();
private Optional<EvaluationInstructions> evaluation = Optional.empty();

@Override
public VersionMatch versionMatch() {
throw new UnsupportedOperationException();
return getOrFail(version);
}

@Override
public ValidityPeriod validityPeriod() {
throw new UnsupportedOperationException();
return getOrFail(period);
}

@Override
public String feature() {
throw new UnsupportedOperationException();
return getOrFail(feature);
}

@Override
public EvaluationInstructions evaluationInstructions() {
return getOrFail(evaluation);
}

private <T> T getOrFail(Optional<T> optional) {
if (optional.isPresent()) {
return optional.get();
}
throw new UnsupportedOperationException();
}

public FakeCondition withVersionMatch(VersionMatch v) {
this.version = Optional.ofNullable(v);
return this;
}

public FakeCondition withValidityPeriod(ValidityPeriod p) {
this.period = Optional.ofNullable(p);
return this;
}

public FakeCondition withFeature(String f) {
this.feature = Optional.ofNullable(f);
return this;
}

public FakeCondition withEvaluationInstructions(EvaluationInstructions e) {
this.evaluation = Optional.ofNullable(e);
return this;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import org.eclipse.passage.lic.internal.api.conditions.evaluation.ParsedExpression;

@SuppressWarnings("restriction")
public class FakeConditionExpressionEvaluator implements ExpressionEvaluationService {
public class FakeExpressionEvaluationService implements ExpressionEvaluationService {

@Override
public ExpressionProtocol id() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/*******************************************************************************
* Copyright (c) 2020 ArSysOp
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* https://www.eclipse.org/legal/epl-2.0/.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* ArSysOp - initial API and implementation
*******************************************************************************/
package org.eclipse.passage.lic.internal.base.tests.conditions.evaluation;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.Collections;

import org.eclipse.passage.lic.api.tests.fakes.conditions.FakeCondition;
import org.eclipse.passage.lic.api.tests.fakes.conditions.evaluation.FakeExpressionTokenAssessmentService;
import org.eclipse.passage.lic.internal.api.LicensedProduct;
import org.eclipse.passage.lic.internal.api.conditions.Condition;
import org.eclipse.passage.lic.internal.api.conditions.EvaluationType;
import org.eclipse.passage.lic.internal.api.conditions.ValidityPeriod;
import org.eclipse.passage.lic.internal.api.conditions.evaluation.Emission;
import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionEvaluatorsRegistry;
import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionPasringRegistry;
import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionTokenAssessorsRegistry;
import org.eclipse.passage.lic.internal.api.conditions.evaluation.PermissionEmittingService;
import org.eclipse.passage.lic.internal.api.diagnostic.code.LicenseCheckFailed;
import org.eclipse.passage.lic.internal.api.diagnostic.code.LicenseDoesNotMatch;
import org.eclipse.passage.lic.internal.api.diagnostic.code.LicenseInvalid;
import org.eclipse.passage.lic.internal.base.BaseLicensedProduct;
import org.eclipse.passage.lic.internal.base.conditions.BaseEvaluationInstructions;
import org.eclipse.passage.lic.internal.base.conditions.BaseValidityPeriodClosed;
import org.eclipse.passage.lic.internal.base.conditions.evaluation.AndsProtocolExpressionParseService;
import org.eclipse.passage.lic.internal.base.conditions.evaluation.BasePermissionEmittingService;
import org.eclipse.passage.lic.internal.base.conditions.evaluation.SimpleMapExpressionEvaluationService;
import org.eclipse.passage.lic.internal.base.registry.ReadOnlyRegistry;
import org.junit.Test;

@SuppressWarnings("restriction")
public final class BasePermissionEmittingServiceTest {

@Test(expected = NullPointerException.class)
public void demandsParsers() {
new BasePermissionEmittingService(null, assessors(), evaluators());
}

@Test(expected = NullPointerException.class)
public void demandsAssessors() {
new BasePermissionEmittingService(parsers(), null, evaluators());
}

@Test(expected = NullPointerException.class)
public void demandsEvluators() {
new BasePermissionEmittingService(parsers(), assessors(), null);
}

@Test
public void singleConditionFailureIsContagious() {
BiasedAssessor morphology = morphologyAssessor(2, 1);
BiasedAssessor dialog = dialogAssessor(4, 3);
Emission emission = service(morphology, dialog)//
.emit(Arrays.asList(humanoid(2, 1), teller(5, 3)), product());
assertTrue(emission.failed());
assertTrue(emission.failureDiagnostic().troubles().size() == 1);
assertEquals(2, morphology.askedKeys().size());
assertTrue(dialog.askedKeys().contains("storytelling"));//$NON-NLS-1$
}

@Test
public void differentEvaluationTypes() {
// given: conditions of different evaluation types
BiasedAssessor morphology = morphologyAssessor(2, 1);
BiasedAssessor dialog = dialogAssessor(4, 3);
// when: emit on valid data
Emission emission = service(morphology, dialog)//
.emit(Arrays.asList(//
humanoid(2, 1), //
teller(4, 3)//
), product());
// then: all conditions evaluated to permissions
assertFalse(emission.failed());
assertEquals(2, emission.permissions().size());
assertEquals(2, morphology.askedKeys().size());
assertEquals(2, dialog.askedKeys().size());
}

@Test
public void unsupportedEvaluationTypeFailsEmission() {
BiasedAssessor morphology = morphologyAssessor(4, 2);
Emission emission = service(morphology).emit(Arrays.asList(teller(4, 3)), product());
assertTrue(emission.failed());
assertEquals(1, emission.failureDiagnostic().troubles().size());
assertEquals(new LicenseCheckFailed(), emission.failureDiagnostic().troubles().get(0).code());
}

@Test
public void corruptedExpressionFailsEmission() {
Emission emission = service(morphologyAssessor(1, 1)).emit(Arrays.asList(corrupted()), product());
assertTrue(emission.failed());
assertEquals(new LicenseInvalid(), emission.failureDiagnostic().troubles().get(0).code());
}

@Test
public void negativeAssessmentExpressionFailsEmission() {
Emission emission = service(morphologyAssessor(1, 1)).emit(Arrays.asList(humanoid(2, 1)), product());
assertTrue(emission.failed());
assertEquals(new LicenseDoesNotMatch(), emission.failureDiagnostic().troubles().get(0).code());
}

private PermissionEmittingService service(BiasedAssessor... assessors) {
return new BasePermissionEmittingService(parsers(), assessors(assessors), evaluators());
}

private ExpressionPasringRegistry parsers() {
return () -> new ReadOnlyRegistry<>(Collections.singleton(new AndsProtocolExpressionParseService()));
}

private ExpressionTokenAssessorsRegistry assessors() {
return () -> new ReadOnlyRegistry<>(Collections.singleton(new FakeExpressionTokenAssessmentService()));
}

private ExpressionEvaluatorsRegistry evaluators() {
return () -> new ReadOnlyRegistry<>(Collections.singleton(new SimpleMapExpressionEvaluationService()));
}

private ExpressionTokenAssessorsRegistry assessors(BiasedAssessor... assessors) {
return () -> new ReadOnlyRegistry<>(Arrays.asList(assessors));
}

private LicensedProduct product() {
return new BaseLicensedProduct("test-condition-evaluation", "1.2.64"); //$NON-NLS-1$//$NON-NLS-2$
}

private Condition corrupted() {
return new FakeCondition().withEvaluationInstructions(//
new BaseEvaluationInstructions(//
new EvaluationType.Of("morphology"), //$NON-NLS-1$
"legs=;eyes=1")) //$NON-NLS-1$
.withValidityPeriod(hour());
}

private Condition humanoid(int legs, int eyes) {
return new FakeCondition().withEvaluationInstructions(//
new BaseEvaluationInstructions(//
new EvaluationType.Of("morphology"), //$NON-NLS-1$
"legs=" + legs + ";eyes=" + eyes)) //$NON-NLS-1$//$NON-NLS-2$
.withValidityPeriod(hour());
}

private Condition teller(int storytelling, int synonims) {
return new FakeCondition().withEvaluationInstructions(//
new BaseEvaluationInstructions(//
new EvaluationType.Of("dialog"), //$NON-NLS-1$
"storytelling=" + storytelling + ";synonims=" + synonims)) //$NON-NLS-1$//$NON-NLS-2$
.withValidityPeriod(hour());
}

private ValidityPeriod hour() {
return new BaseValidityPeriodClosed(//
ZonedDateTime.now(), //
ZonedDateTime.now().plusHours(1));
}

private BiasedAssessor morphologyAssessor(int legs, int eyes) {
return new BiasedAssessor("morphology")// //$NON-NLS-1$
.withAnswer("legs", Integer.toString(legs)) //$NON-NLS-1$
.withAnswer("eyes", Integer.toString(eyes)); //$NON-NLS-1$
}

private BiasedAssessor dialogAssessor(int storytelling, int synonims) {
return new BiasedAssessor("dialog")// //$NON-NLS-1$
.withAnswer("storytelling", Integer.toString(storytelling)) //$NON-NLS-1$
.withAnswer("synonims", Integer.toString(synonims)); //$NON-NLS-1$
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*******************************************************************************
* Copyright (c) 2020 ArSysOp
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* https://www.eclipse.org/legal/epl-2.0/.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* ArSysOp - initial API and implementation
*******************************************************************************/
package org.eclipse.passage.lic.internal.base.tests.conditions.evaluation;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.eclipse.passage.lic.internal.api.conditions.EvaluationType;
import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionEvaluationException;
import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionTokenAssessmentService;

@SuppressWarnings("restriction")
final class BiasedAssessor implements ExpressionTokenAssessmentService {

private final Map<String, String> answers = new HashMap<>();
private final Set<String> asked = new HashSet<>();
private final EvaluationType type;

BiasedAssessor(String type) {
this.type = new EvaluationType.Of(type);
}

BiasedAssessor() {
this("biased"); //$NON-NLS-1$
}

BiasedAssessor withAnswer(String key, String answer) {
answers.put(key, answer);
return this;
}

@Override
public EvaluationType id() {
return type;
}

@Override
public boolean equal(String key, String value) throws ExpressionEvaluationException {
asked.add(key);
return answers.containsKey(key) ? answers.get(key).equals(value) : false;
}

Set<String> askedKeys() {
return asked;
}
}
Loading

0 comments on commit c2706be

Please sign in to comment.