Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug 565011 rebuild the core of condition expression evaluation #300

Merged
merged 1 commit into from
Jul 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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