blob: c0a21927a664ae53ddee6434b84e562bcaf84a44 [file] [log] [blame]
// Copyright (C) 2011 The Android Open Source Project
//
// 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 com.google.gerrit.rules;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.testutil.GerritBaseTests;
import com.google.inject.Guice;
import com.google.inject.Module;
import com.googlecode.prolog_cafe.exceptions.CompileException;
import com.googlecode.prolog_cafe.lang.BufferingPrologControl;
import com.googlecode.prolog_cafe.lang.JavaObjectTerm;
import com.googlecode.prolog_cafe.lang.Prolog;
import com.googlecode.prolog_cafe.lang.PrologClassLoader;
import com.googlecode.prolog_cafe.lang.PrologMachineCopy;
import com.googlecode.prolog_cafe.lang.StructureTerm;
import com.googlecode.prolog_cafe.lang.SymbolTerm;
import com.googlecode.prolog_cafe.lang.Term;
import com.googlecode.prolog_cafe.lang.VariableTerm;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PushbackReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/** Base class for any tests written in Prolog. */
public abstract class PrologTestCase extends GerritBaseTests {
private static final SymbolTerm test_1 = SymbolTerm.intern("test", 1);
private String pkg;
private boolean hasSetup;
private boolean hasTeardown;
private List<Term> tests;
protected PrologMachineCopy machine;
protected PrologEnvironment.Factory envFactory;
protected void load(String pkg, String prologResource, Module... modules)
throws CompileException, IOException {
ArrayList<Module> moduleList = new ArrayList<>();
moduleList.add(new PrologModule.EnvironmentModule());
moduleList.addAll(Arrays.asList(modules));
envFactory = Guice.createInjector(moduleList).getInstance(PrologEnvironment.Factory.class);
PrologEnvironment env = envFactory.create(newMachine());
consult(env, getClass(), prologResource);
this.pkg = pkg;
hasSetup = has(env, "setup");
hasTeardown = has(env, "teardown");
StructureTerm head =
new StructureTerm(
":", SymbolTerm.intern(pkg), new StructureTerm(test_1, new VariableTerm()));
tests = new ArrayList<>();
for (Term[] pair : env.all(Prolog.BUILTIN, "clause", head, new VariableTerm())) {
tests.add(pair[0]);
}
assertThat(tests).isNotEmpty();
machine = PrologMachineCopy.save(env);
}
/**
* Set up the Prolog environment.
*
* @param env Prolog environment.
*/
protected void setUpEnvironment(PrologEnvironment env) throws Exception {}
private PrologMachineCopy newMachine() {
BufferingPrologControl ctl = new BufferingPrologControl();
ctl.setMaxDatabaseSize(16 * 1024);
ctl.setPrologClassLoader(new PrologClassLoader(getClass().getClassLoader()));
return PrologMachineCopy.save(ctl);
}
protected void consult(BufferingPrologControl env, Class<?> clazz, String prologResource)
throws CompileException, IOException {
try (InputStream in = clazz.getResourceAsStream(prologResource)) {
if (in == null) {
throw new FileNotFoundException(prologResource);
}
SymbolTerm pathTerm = SymbolTerm.create(prologResource);
JavaObjectTerm inTerm =
new JavaObjectTerm(
new PushbackReader(
new BufferedReader(new InputStreamReader(in, UTF_8)), Prolog.PUSHBACK_SIZE));
if (!env.execute(Prolog.BUILTIN, "consult_stream", pathTerm, inTerm)) {
throw new CompileException("Cannot consult " + prologResource);
}
}
}
private boolean has(BufferingPrologControl env, String name) {
StructureTerm head = SymbolTerm.create(pkg, name, 0);
return env.execute(Prolog.BUILTIN, "clause", head, new VariableTerm());
}
public void runPrologBasedTests() throws Exception {
int errors = 0;
long start = TimeUtil.nowMs();
for (Term test : tests) {
PrologEnvironment env = envFactory.create(machine);
setUpEnvironment(env);
env.setEnabled(Prolog.Feature.IO, true);
System.out.format("Prolog %-60s ...", removePackage(test));
System.out.flush();
if (hasSetup) {
call(env, "setup");
}
List<Term> all = env.all(Prolog.BUILTIN, "call", test);
if (hasTeardown) {
call(env, "teardown");
}
System.out.println(all.size() == 1 ? "OK" : "FAIL");
if (all.size() > 0 && !test.equals(all.get(0))) {
for (Term t : all) {
Term head = ((StructureTerm) removePackage(t)).args()[0];
Term[] args = ((StructureTerm) head).args();
System.out.print(" Result: ");
for (int i = 0; i < args.length; i++) {
if (0 < i) {
System.out.print(", ");
}
System.out.print(args[i]);
}
System.out.println();
}
System.out.println();
}
if (all.size() != 1) {
errors++;
}
}
long end = TimeUtil.nowMs();
System.out.println("-------------------------------");
System.out.format(
"Prolog tests: %d, Failures: %d, Time elapsed %.3f sec",
tests.size(), errors, (end - start) / 1000.0);
System.out.println();
assertThat(errors).isEqualTo(0);
}
private void call(BufferingPrologControl env, String name) {
StructureTerm head = SymbolTerm.create(pkg, name, 0);
assertWithMessage("Cannot invoke " + pkg + ":" + name)
.that(env.execute(Prolog.BUILTIN, "call", head))
.isTrue();
}
private Term removePackage(Term test) {
Term name = test;
if (name instanceof StructureTerm && ":".equals(name.name())) {
name = name.arg(1);
}
return name;
}
}