| // 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.server.rules.prolog; | |
| 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.server.util.time.TimeUtil; | |
| 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; | |
| import org.junit.Ignore; | |
| /** Base class for any tests written in Prolog. */ | |
| @Ignore | |
| public abstract class PrologTestCase { | |
| 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; | |
| } | |
| } |