Port posix/kotlin tests to pytest.

This commit is contained in:
Cornelius Riemenschneider
2024-08-18 23:10:45 +02:00
parent c2aff1ea97
commit ccd90f25ba
7 changed files with 234 additions and 159 deletions

View File

@@ -1,4 +1,6 @@
from create_database_utils import *
import runs_on
os.mkdir('build')
run_codeql_database_create(["kotlinc test.kt -d build", "javac User.java -cp build"], lang="java")
@runs_on.posix
def test(codeql, java_full):
codeql.database.create(command=["kotlinc test.kt -d build", "javac User.java -cp build"])

View File

@@ -1,128 +1,178 @@
from create_database_utils import *
import os
import os.path
import subprocess
import commands
import runs_on
# Build a family of dependencies outside tracing, then refer to them from a traced build:
older_datetime = "202201010101"
newer_datetime = "202202020202"
@runs_on.posix
def test(codeql, java_full):
# Build a family of dependencies outside tracing, then refer to them from a traced build:
classpath_entries = dict()
older_datetime = "202201010101"
newer_datetime = "202202020202"
extraction_orders = ["JavaSeesFirst", "KotlinSeesFirst"]
jar_states = ["NoJar", "JarMtimesEqual", "JavaJarNewer", "KotlinJarNewer"]
class_file_states = ["ClassFileMtimesEqual", "JavaClassFileNewer", "KotlinClassFileNewer"]
classpath_entries = dict()
# Create test classes for each combination of which extractor will see the file first, the relative timestamps of the jar files seen by each, and the relative timestamps of the class file inside:
extraction_orders = ["JavaSeesFirst", "KotlinSeesFirst"]
jar_states = ["NoJar", "JarMtimesEqual", "JavaJarNewer", "KotlinJarNewer"]
class_file_states = ["ClassFileMtimesEqual", "JavaClassFileNewer", "KotlinClassFileNewer"]
jobs = []
# Create test classes for each combination of which extractor will see the file first, the relative timestamps of the jar files seen by each, and the relative timestamps of the class file inside:
for first_extraction in extraction_orders:
for jar_state in jar_states:
for class_file_state in class_file_states:
dep_dir = os.path.join(first_extraction, jar_state, class_file_state)
dep_classname = "Dep___%s___%s___%s" % (first_extraction, jar_state, class_file_state)
dep_seen_by_java_dir = os.path.join(dep_dir, "seen_by_java")
dep_seen_by_kotlin_dir = os.path.join(dep_dir, "seen_by_kotlin")
os.makedirs(dep_seen_by_java_dir)
os.makedirs(dep_seen_by_kotlin_dir)
dep_seen_by_java_sourcefile = os.path.join(dep_seen_by_java_dir, dep_classname + ".java")
dep_seen_by_kotlin_sourcefile = os.path.join(dep_seen_by_kotlin_dir, dep_classname + ".java")
with open(dep_seen_by_java_sourcefile, "w") as f:
f.write("public class %s { }" % dep_classname)
with open(dep_seen_by_kotlin_sourcefile, "w") as f:
f.write("public class %s { void memberOnlySeenByKotlin() { } }" % dep_classname)
jobs.append({
"first_extraction": first_extraction,
"jar_state": jar_state,
"class_file_state": class_file_state,
"dep_dir": dep_dir,
"dep_classname": dep_classname,
"dep_seen_by_java_dir": dep_seen_by_java_dir,
"dep_seen_by_kotlin_dir": dep_seen_by_kotlin_dir,
"dep_seen_by_java_sourcefile": dep_seen_by_java_sourcefile,
"dep_seen_by_kotlin_sourcefile": dep_seen_by_kotlin_sourcefile
})
jobs = []
# Compile all the test classes we just generated, in two commands (since javac objects to seeing the same class file twice in one run)
for first_extraction in extraction_orders:
for jar_state in jar_states:
for class_file_state in class_file_states:
dep_dir = os.path.join(first_extraction, jar_state, class_file_state)
dep_classname = "Dep___%s___%s___%s" % (
first_extraction,
jar_state,
class_file_state,
)
dep_seen_by_java_dir = os.path.join(dep_dir, "seen_by_java")
dep_seen_by_kotlin_dir = os.path.join(dep_dir, "seen_by_kotlin")
os.makedirs(dep_seen_by_java_dir)
os.makedirs(dep_seen_by_kotlin_dir)
dep_seen_by_java_sourcefile = os.path.join(
dep_seen_by_java_dir, dep_classname + ".java"
)
dep_seen_by_kotlin_sourcefile = os.path.join(
dep_seen_by_kotlin_dir, dep_classname + ".java"
)
with open(dep_seen_by_java_sourcefile, "w") as f:
f.write("public class %s { }" % dep_classname)
with open(dep_seen_by_kotlin_sourcefile, "w") as f:
f.write("public class %s { void memberOnlySeenByKotlin() { } }" % dep_classname)
jobs.append(
{
"first_extraction": first_extraction,
"jar_state": jar_state,
"class_file_state": class_file_state,
"dep_dir": dep_dir,
"dep_classname": dep_classname,
"dep_seen_by_java_dir": dep_seen_by_java_dir,
"dep_seen_by_kotlin_dir": dep_seen_by_kotlin_dir,
"dep_seen_by_java_sourcefile": dep_seen_by_java_sourcefile,
"dep_seen_by_kotlin_sourcefile": dep_seen_by_kotlin_sourcefile,
}
)
subprocess.check_call(["javac"] + [j["dep_seen_by_java_sourcefile"] for j in jobs])
subprocess.check_call(["javac"] + [j["dep_seen_by_kotlin_sourcefile"] for j in jobs])
# Compile all the test classes we just generated, in two commands (since javac objects to seeing the same class file twice in one run)
# Create jar files and set class and jar files' relative timestamps for each dependency the two extractors will see:
commands.run(["javac"] + [j["dep_seen_by_java_sourcefile"] for j in jobs])
commands.run(["javac"] + [j["dep_seen_by_kotlin_sourcefile"] for j in jobs])
for j in jobs:
os.remove(j["dep_seen_by_java_sourcefile"])
os.remove(j["dep_seen_by_kotlin_sourcefile"])
dep_seen_by_java_classfile = j["dep_seen_by_java_sourcefile"].replace(".java", ".class")
dep_seen_by_kotlin_classfile = j["dep_seen_by_kotlin_sourcefile"].replace(".java", ".class")
# Create jar files and set class and jar files' relative timestamps for each dependency the two extractors will see:
subprocess.check_call(["touch", "-t", newer_datetime if j["class_file_state"] == "JavaClassFileNewer" else older_datetime, dep_seen_by_java_classfile])
subprocess.check_call(["touch", "-t", newer_datetime if j["class_file_state"] == "KotlinClassFileNewer" else older_datetime, dep_seen_by_kotlin_classfile])
for j in jobs:
os.remove(j["dep_seen_by_java_sourcefile"])
os.remove(j["dep_seen_by_kotlin_sourcefile"])
dep_seen_by_java_classfile = j["dep_seen_by_java_sourcefile"].replace(".java", ".class")
dep_seen_by_kotlin_classfile = j["dep_seen_by_kotlin_sourcefile"].replace(".java", ".class")
if j["jar_state"] != "NoJar":
classfile_name = os.path.basename(dep_seen_by_java_classfile)
jar_command = ["jar", "cf", "dep.jar", classfile_name]
subprocess.check_call(jar_command, cwd = j["dep_seen_by_java_dir"])
subprocess.check_call(jar_command, cwd = j["dep_seen_by_kotlin_dir"])
jar_seen_by_java = os.path.join(j["dep_seen_by_java_dir"], "dep.jar")
jar_seen_by_kotlin = os.path.join(j["dep_seen_by_kotlin_dir"], "dep.jar")
subprocess.check_call(["touch", "-t", newer_datetime if j["jar_state"] == "JavaJarNewer" else older_datetime, jar_seen_by_java])
subprocess.check_call(["touch", "-t", newer_datetime if j["jar_state"] == "KotlinJarNewer" else older_datetime, jar_seen_by_kotlin])
j["javac_classpath_entry"] = jar_seen_by_java
j["kotlinc_classpath_entry"] = jar_seen_by_kotlin
else:
# No jar file involved, just add the dependency build directory to the classpath:
j["javac_classpath_entry"] = j["dep_seen_by_java_dir"]
j["kotlinc_classpath_entry"] = j["dep_seen_by_kotlin_dir"]
commands.run(
[
"touch",
"-t",
newer_datetime if j["class_file_state"] == "JavaClassFileNewer" else older_datetime,
dep_seen_by_java_classfile,
]
)
commands.run(
[
"touch",
"-t",
(
newer_datetime
if j["class_file_state"] == "KotlinClassFileNewer"
else older_datetime
),
dep_seen_by_kotlin_classfile,
]
)
# Create source files that instantiate each dependency type:
if j["jar_state"] != "NoJar":
classfile_name = os.path.basename(dep_seen_by_java_classfile)
jar_command = ["jar", "cf", "dep.jar", classfile_name]
commands.run(jar_command, _cwd=j["dep_seen_by_java_dir"])
commands.run(jar_command, _cwd=j["dep_seen_by_kotlin_dir"])
jar_seen_by_java = os.path.join(j["dep_seen_by_java_dir"], "dep.jar")
jar_seen_by_kotlin = os.path.join(j["dep_seen_by_kotlin_dir"], "dep.jar")
commands.run(
[
"touch",
"-t",
newer_datetime if j["jar_state"] == "JavaJarNewer" else older_datetime,
jar_seen_by_java,
]
)
commands.run(
[
"touch",
"-t",
newer_datetime if j["jar_state"] == "KotlinJarNewer" else older_datetime,
jar_seen_by_kotlin,
]
)
j["javac_classpath_entry"] = jar_seen_by_java
j["kotlinc_classpath_entry"] = jar_seen_by_kotlin
else:
# No jar file involved, just add the dependency build directory to the classpath:
j["javac_classpath_entry"] = j["dep_seen_by_java_dir"]
j["kotlinc_classpath_entry"] = j["dep_seen_by_kotlin_dir"]
kotlin_first_jobs = [j for j in jobs if j["first_extraction"] == "KotlinSeesFirst"]
java_first_jobs = [j for j in jobs if j["first_extraction"] == "JavaSeesFirst"]
kotlin_first_classes = [j["dep_classname"] for j in kotlin_first_jobs]
java_first_classes = [j["dep_classname"] for j in java_first_jobs]
# Create source files that instantiate each dependency type:
kotlin_first_user = "kotlinFirstUser.kt"
kotlin_second_user = "kotlinSecondUser.kt"
java_first_user = "JavaFirstUser.java"
java_second_user = "JavaSecondUser.java"
kotlin_first_jobs = [j for j in jobs if j["first_extraction"] == "KotlinSeesFirst"]
java_first_jobs = [j for j in jobs if j["first_extraction"] == "JavaSeesFirst"]
kotlin_first_classes = [j["dep_classname"] for j in kotlin_first_jobs]
java_first_classes = [j["dep_classname"] for j in java_first_jobs]
def kotlin_instantiate_classes(classes):
return "; ".join(["noop(%s())" % c for c in classes])
kotlin_first_user = "kotlinFirstUser.kt"
kotlin_second_user = "kotlinSecondUser.kt"
java_first_user = "JavaFirstUser.java"
java_second_user = "JavaSecondUser.java"
def make_kotlin_user(user_filename, classes):
with open(user_filename, "w") as f:
f.write("fun noop(x: Any) { } fun user() { %s }" % kotlin_instantiate_classes(classes))
def kotlin_instantiate_classes(classes):
return "; ".join(["noop(%s())" % c for c in classes])
make_kotlin_user(kotlin_first_user, kotlin_first_classes)
make_kotlin_user(kotlin_second_user, java_first_classes)
def make_kotlin_user(user_filename, classes):
with open(user_filename, "w") as f:
f.write("fun noop(x: Any) { } fun user() { %s }" % kotlin_instantiate_classes(classes))
def java_instantiate_classes(classes):
return " ".join(["noop(new %s());" % c for c in classes])
make_kotlin_user(kotlin_first_user, kotlin_first_classes)
make_kotlin_user(kotlin_second_user, java_first_classes)
def make_java_user(user_filename, classes):
with open(user_filename, "w") as f:
f.write("public class %s { private static void noop(Object x) { } public static void user() { %s } }" % (user_filename.replace(".java", ""), java_instantiate_classes(classes)))
def java_instantiate_classes(classes):
return " ".join(["noop(new %s());" % c for c in classes])
make_java_user(java_first_user, java_first_classes)
make_java_user(java_second_user, kotlin_first_classes)
def make_java_user(user_filename, classes):
with open(user_filename, "w") as f:
f.write(
"public class %s { private static void noop(Object x) { } public static void user() { %s } }"
% (user_filename.replace(".java", ""), java_instantiate_classes(classes))
)
# Now finally make a database, including classes where Java sees them first followed by Kotlin and vice versa.
# In all cases the Kotlin extraction should take precedence.
make_java_user(java_first_user, java_first_classes)
make_java_user(java_second_user, kotlin_first_classes)
def make_classpath(jobs, entry_name):
return ":".join([j[entry_name] for j in jobs])
# Now finally make a database, including classes where Java sees them first followed by Kotlin and vice versa.
# In all cases the Kotlin extraction should take precedence.
kotlin_first_classpath = make_classpath(kotlin_first_jobs, "kotlinc_classpath_entry")
java_first_classpath = make_classpath(java_first_jobs, "javac_classpath_entry")
kotlin_second_classpath = make_classpath(java_first_jobs, "kotlinc_classpath_entry")
java_second_classpath = make_classpath(kotlin_first_jobs, "javac_classpath_entry")
def make_classpath(jobs, entry_name):
return ":".join([j[entry_name] for j in jobs])
run_codeql_database_create([
"kotlinc -cp %s %s" % (kotlin_first_classpath, kotlin_first_user),
"javac -cp %s %s" % (java_first_classpath, java_first_user),
"kotlinc -cp %s %s" % (kotlin_second_classpath, kotlin_second_user),
"javac -cp %s %s" % (java_second_classpath, java_second_user)
], lang="java")
kotlin_first_classpath = make_classpath(kotlin_first_jobs, "kotlinc_classpath_entry")
java_first_classpath = make_classpath(java_first_jobs, "javac_classpath_entry")
kotlin_second_classpath = make_classpath(java_first_jobs, "kotlinc_classpath_entry")
java_second_classpath = make_classpath(kotlin_first_jobs, "javac_classpath_entry")
codeql.database.create(
command=[
"kotlinc -cp %s %s" % (kotlin_first_classpath, kotlin_first_user),
"javac -cp %s %s" % (java_first_classpath, java_first_user),
"kotlinc -cp %s %s" % (kotlin_second_classpath, kotlin_second_user),
"javac -cp %s %s" % (java_second_classpath, java_second_user),
]
)

View File

@@ -1,41 +0,0 @@
#!/usr/bin/env python3
import glob
import os
import re
from create_database_utils import *
def say(s):
print(s)
sys.stdout.flush()
say('Doing normal compilation')
# This is a normal intercepted compilation
runSuccessfully([get_cmd('kotlinc'), 'normal.kt'])
say('Identifying extractor jar')
# Find the extractor jar that is being used
trapDir = os.environ['CODEQL_EXTRACTOR_JAVA_TRAP_DIR']
invocationTrapDir = os.path.join(trapDir, 'invocations')
invocationTraps = os.listdir(invocationTrapDir)
if len(invocationTraps) != 1:
raise Exception('Expected to find 1 invocation TRAP, but found ' + str(invocationTraps))
invocationTrap = os.path.join(invocationTrapDir, invocationTraps[0])
with open(invocationTrap, 'r') as f:
content = f.read()
m = re.search('^// Using extractor: (.*)$', content, flags = re.MULTILINE)
extractorJar = m.group(1)
def getManualFlags(invocationTrapName):
return ['-Xplugin=' + extractorJar, '-P', 'plugin:kotlin-extractor:invocationTrapFile=' + os.path.join(trapDir, 'invocations', invocationTrapName + '.trap')]
# This is both normally intercepted, and it has the extractor flags manually added
say('Doing double-interception compilation')
runSuccessfully([get_cmd('kotlinc'), 'doubleIntercepted.kt'] + getManualFlags('doubleIntercepted'))
os.environ['CODEQL_EXTRACTOR_JAVA_AGENT_DISABLE_KOTLIN'] = 'true'
# We don't see this compilation at all
say('Doing unseen compilation')
runSuccessfully([get_cmd('kotlinc'), 'notSeen.kt'])
# This is extracted as it has the extractor flags manually added
say('Doing manual compilation')
runSuccessfully([get_cmd('kotlinc'), 'manual.kt'] + getManualFlags('manual'))

View File

@@ -1,7 +1,59 @@
from build_fixture import build_fixture
import sys
import commands
import os
import pathlib
import re
import runs_on
from create_database_utils import *
run_codeql_database_create(
['"%s" build.py' % sys.executable],
source="code", lang="java")
def say(s):
print(s)
sys.stdout.flush()
@build_fixture
def build():
say("Doing normal compilation")
# This is a normal intercepted compilation
commands.run("kotlinc normal.kt")
say("Identifying extractor jar")
# Find the extractor jar that is being used
trapDir = pathlib.Path(os.environ["CODEQL_EXTRACTOR_JAVA_TRAP_DIR"])
invocationTrapDir = trapDir / "invocations"
invocationTraps = list(invocationTrapDir.iterdir())
assert len(invocationTraps) == 1, "Expected to find 1 invocation TRAP, but found " + str(
invocationTraps
)
invocationTrap = os.path.join(invocationTrapDir, invocationTraps[0])
with open(invocationTrap, "r") as f:
content = f.read()
m = re.search("^// Using extractor: (.*)$", content, flags=re.MULTILINE)
extractorJar = m.group(1)
def getManualFlags(invocationTrapName):
return [
f"-Xplugin={extractorJar}",
"-P",
f"plugin:kotlin-extractor:invocationTrapFile={trapDir / "invocations" / invocationTrapName}.trap",
]
# This is both normally intercepted, and it has the extractor flags manually added
say("Doing double-interception compilation")
commands.run(["kotlinc", "doubleIntercepted.kt"] + getManualFlags("doubleIntercepted"))
os.environ["CODEQL_EXTRACTOR_JAVA_AGENT_DISABLE_KOTLIN"] = "true"
# We don't see this compilation at all
say("Doing unseen compilation")
commands.run("kotlinc notSeen.kt")
# This is extracted as it has the extractor flags manually added
say("Doing manual compilation")
commands.run(["kotlinc", "manual.kt"] + getManualFlags("manual"))
@runs_on.posix
def test(codeql, java_full, build):
codeql.database.create(command=build, source_root="code")

View File

@@ -1,3 +1,13 @@
from create_database_utils import *
import runs_on
run_codeql_database_create(["kotlinc test1.kt", "kotlinc test2.kt -module-name mymodule", "kotlinc test3.kt -module-name reservedchars\\\"${}/", "javac User.java -cp ." ], lang="java")
@runs_on.posix
def test(codeql, java_full):
codeql.database.create(
command=[
"kotlinc test1.kt",
"kotlinc test2.kt -module-name mymodule",
'kotlinc test3.kt -module-name reservedchars\\"${}/',
"javac User.java -cp .",
]
)

View File

@@ -1,5 +1,12 @@
from create_database_utils import *
import runs_on
os.mkdir('build1')
os.mkdir('build2')
run_codeql_database_create(["kotlinc kConsumer.kt -d build1", "javac Test.java -cp build1 -d build2", "kotlinc user.kt -cp build1:build2"], lang="java")
@runs_on.posix
def test(codeql, java_full):
codeql.database.create(
command=[
"kotlinc kConsumer.kt -d build1",
"javac Test.java -cp build1 -d build2",
"kotlinc user.kt -cp build1:build2",
]
)

View File

@@ -1,5 +0,0 @@
dependencies:
codeql/java-all: '*'
codeql/java-tests: '*'
codeql/java-queries: '*'
warnOnImplicitThis: true