No runtime coverage using emma, junit and ant - java

I have the following project structure:
src/com/dummy/abc.java
src_tests/come/dummy/abcTest.java
build.xml
I need to check the coverage of my code done by tests using emma.
From reading the emma + junit examples i came to a conclusion that to get a report i need to do the following:
compile 'src'
compile 'src_tests'
instrument compiled 'src_tests' => 'instrumented_src_tests'
run junit on 'instrumented_src_tests' with additional jvmarg
The problem is that the step 4 should return some kind of a file which then used with 'report' command should create a report. I'm getting
emma-report:
[report] processing input files ...
[report] 1 file(s) read and merged in 67 ms
[report] nothing to do: no runtime coverage data found in any of the data files
~ edit
i'm attaching my build.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="HELL scream" default="all" basedir=".">
<property name="build.sources.dir" location="${basedir}/src"/>
<property name="build.sources.des" location="${basedir}/bin/classes"/>
<property name="test.sources.dir" location="${basedir}/src_test"/>
<property name="test.sources.des" location="${basedir}/bin/classes_test"/>
<property name="test.reports.des" location="${basedir}/reports-junit"/>
<property name="emma.sources.des" location="${basedir}/bin/classes_emma"/>
<property name="emma.reports.des" location="${basedir}/reports-emma"/>
<property name="emma.final.reports.des" location="${basedir}/reports-emma/final"/>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<path id="emma.lib" >
<fileset dir="/home/user1/Desktop/emma-2.0.5312/lib">
<include name="*.jar"/>
</fileset>
</path>
<taskdef resource="emma_ant.properties" classpathref="emma.lib" />
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<target name="clean-all">
<delete failonerror="false">
<fileset dir="${emma.final.reports.des}"/>
<fileset dir="${emma.reports.des}"/>
<fileset dir="${emma.sources.des}"/>
<fileset dir="${test.reports.des}"/>
<fileset dir="${test.sources.des}"/>
<fileset dir="${build.sources.des}"/>
</delete>
</target>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<target name="compile-sources">
<mkdir dir="${build.sources.des}"/>
<javac srcdir="${build.sources.dir}" includes="" excludes="" destdir="${build.sources.des}" listfiles="true" debug="true" includeantruntime="false"/>
</target>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<target name="compile-tests">
<mkdir dir="${test.sources.des}"/>
<javac srcdir="${test.sources.dir}" includes="" excludes="" destdir="${test.sources.des}" listfiles="true" debug="true" includeantruntime="false">
<classpath>
<pathelement location="/home/user1/Desktop/junit-4.10.jar"/>
<pathelement location="${build.sources.des}"/>
</classpath>
</javac>
</target>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<target name="compile-emma-tests">
<emma enabled="true" >
<instr instrpath="${test.sources.des}" destdir="${emma.sources.des}" metadatafile ="${emma.reports.des}/instrumentation.emma" merge ="true"/>
</emma>
</target>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<target name="run-tests">
<mkdir dir="${test.reports.des}"/>
<junit haltonfailure="no" showoutput="yes" printsummary="true">
<formatter type="plain" usefile="false" />
<formatter type="xml"/>
<classpath>
<pathelement location="/home/user1/Desktop/junit-4.10.jar"/>
<pathelement location="${build.sources.des}"/>
<pathelement location="${emma.sources.des}"/>
<path refid="emma.lib" />
</classpath>
<batchtest todir="${test.reports.des}" fork="true">
<fileset dir="${emma.sources.des}"/>
</batchtest>
<jvmarg value="-Demma.coverage.out.file=${emma.reports.des}/coverage.emma" />
<jvmarg value="-Demma.coverage.out.merge=false" />
</junit>
</target>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<target name="junit-tests-report">
<junitreport todir="${test.reports.des}">
<fileset dir="${test.reports.des}">
<include name="TEST-*.xml"/>
</fileset>
<report format="frames" todir="${test.reports.des}/junit_reports"/>
</junitreport>
</target>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<target name="emma-tests-report">
<emma enabled="true" >
<report sourcepath="${build.sources.dir}">
<fileset dir="${emma.reports.des}" >
<include name="*.emma" />
</fileset>
<txt outfile="${emma.final.reports.des}/coverage.txt" depth="package" columns="class,method,block,line,name" />
<xml outfile="${emma.final.reports.des}/coverage.xml" depth="package" />
<html outfile="${emma.final.reports.des}/coverage.html" depth="method" columns="name,class,method,block,line" />
</report>
</emma>
</target>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<target name="all" depends="clean-all, compile-sources, compile-tests, compile-emma-tests, run-tests, junit-tests-report, emma-tests-report"/>
</project>
it is probably something trivial...
also, when using emma.sources.dest
i'm getting this on my (only) test
run-tests:
[junit] Running com.emma.test.MathTest
[junit] Testsuite: com.emma.test.MathTest
[junit] Tests run: 1, Failures: 0, Errors: 1, Time elapsed: 0 sec
[junit] Tests run: 1, Failures: 0, Errors: 1, Time elapsed: 0 sec
[junit]
[junit] Caused an ERROR
[junit] Illegal local variable table length 5 in method com.emma.test.MathTest.<init>()V
[junit] java.lang.ClassFormatError: Illegal local variable table length 5 in method com.emma.test.MathTest.<init>()V
[junit] at java.lang.Class.forName0(Native Method)
[junit] at java.lang.Class.forName(Class.java:188)
[junit]
[junit] Test com.emma.test.MathTest FAILED
~ SOLVED
add this:
<jvmarg value="-XX:-UseSplitVerifier"/>
<jvmarg value="-Demma.coverage.out.file=${emma.reports.des}/coverage.emma" />
<jvmarg value="-Demma.coverage.out.merge=false" />

Emma hasn't had a stable release since 2005, and doesn't play well with newer versions of the JDKs. Cobertura hasn't done any new development since 2011.
Because of that, I no longer recommend to developers to use Emma or Cobertura.
I've switched to JaCoCo for code coverage. It was started by the Emma Eclipse team, and is actively being developed.
As stated in JaCoCo's mission statement.
[...]Two of the best and widely used available open source tools are EMMA and Cobertura. Both tools are not actively maintained by the original authors any more and do not support the current Java versions. Due to the lack of regression tests maintenance and feature additions is difficult.
Therefore we started the JaCoCo project to provide a new standard technology for code coverage analysis in Java VM based environments. [...]
The nice thing about JaCoCo is that it works in a wide variety of environments, and you don't have to instrument your code after compiling. The instrumentation takes place as the tests are being executed. It looks something like this:
<jacoco:coverage destfile="${target.dir}/jacoco.exec">
<junit>
[...]
</junit>
</jacoco>
You then use <jacoco:coverage> to print the coverage report.
Do you have to use Emma? If not, you might better luck getting everything working using JaCoCo.

Emma relies upon bytecode injection (if memory serves me well); but, with Java 7, the code hasn't been updated because the JVM now has a debugging interface.
JaCoCo tests code coverage by attaching to the debugging interface and listening as your code enters methods and passes over the bytecode without injecting checkpoints. It's clearly the superior way to do things (now that it exists) and I doubt that Emma will do the work to fully support Java 7.
I suggest that you "upgrade" to JaCoCo.

You get this error when you use wrong command to generate the report.
Most of the online tutorial is advocating the wrong(may by old) command, even I got this error when I used below command to generate the report:
{JAVA_HOME}\jre\lib\ext>java -cp emma.jar emma report -r html -in coverage.em, {ALFRESCO_HOME}\coverage.ec
EMMA: processing input files ...
EMMA: 1 file(s) read and merged in 60 ms
EMMA: nothing to do: no runtime coverage data found in any of the data files
Correct usage of the command is:
{JAVA_HOME}\jre\lib\ext>java -cp emma.jar emma report -r txt,html -in {JAVA_HOME}\jre\lib\ext\coverage.em -in C:\t1_tempSetup\Alfresco\coverage.ec
EMMA: processing input files ...
EMMA: 2 file(s) read and merged in 70 ms
EMMA: writing [txt] report to [{JAVA_HOME}\jre\lib\ext\coverage.txt] ...
EMMA: writing [html] report to [{JAVA_HOME}\jre\lib\ext\coverage\index.html] ...

Related

Why does Cobertura seem to switch my Eclipse's Java version?

These are the steps that create the error:
Run my unit tests within Eclipse (Cntrl-F11). All 67 succeed and report as such.
Using an Ant build.xml file, run a Cobertura task to generate a coverage report. The task fails stating that a unit test has failed.
When I try to run the unit tests using Eclipse again, I receive the following error:
Unsupported major.minor version 51.0
(These symptoms persist until I use Project -> Clean within Eclipse.)
My Cobertura ant task is:
<target name="report-test" description="Generate a test coverage report." depends="clean, compile">
<taskdef resource="tasks.properties">
<classpath>
<pathelement location="${lib.dir}/cobertura-1.9.4.1/cobertura.jar" />
<pathelement location="${lib.dir}/cobertura-1.9.4.1/lib/asm-3.0.jar" />
<pathelement location="${lib.dir}/cobertura-1.9.4.1/lib/asm-tree-3.0.jar" />
<pathelement location="${lib.dir}/cobertura-1.9.4.1/lib/log4j-1.2.9.jar" />
<pathelement location="${lib.dir}/cobertura-1.9.4.1/lib/jakarta-oro-2.0.8.jar" />
</classpath>
</taskdef>
<cobertura-instrument todir="${build.dir}/cobertura-instrument">
<fileset dir="${build.dir}">
<include name="**/*.class"/>
</fileset>
</cobertura-instrument>
<junit printsummary="yes" fork="true" haltonfailure="yes" showoutput="yes">
<classpath location="${build.dir}/cobertura-instrument"/>
<classpath location="${build.dir}"/>
<classpath refid="classpath.test" />
<sysproperty key="net.sourceforge.cobertura.datafile" file="cobertura.ser"/>
<formatter type="xml" />
<batchtest todir="doc/junit">
<fileset dir="${test.dir}" />
</batchtest>
</junit>
<cobertura-report srcdir="${src.dir}" destdir="doc/coverage" format="xml" />
<delete file="cobertura.ser"/>
</target>
Coberatura hasn't switched Eclipse's Java version.
What it has actually done is recompile the classes using a version of Java that is more recent than the one you are using to run Eclipse. Eclipse can't load those .class files.
The short term solution is to get Eclipse to clean and rebuild the project(s) after running Coberatura ... as you are currently doing.
In the long term, you should either change Eclipse to run using the same Java version as your Ant builds, or change the Ant build file so that it doesn't write the ".class" files into the Eclipse workspace. Or both ... 'cos having something else writing stuff into the Eclipse workspace is going to cause other problems too.

Using Cobertura in unix to generate the code coverage reports

I m using cobertura-1.9.4.1 to generate code coverage reports.First I set the classpath to cobertura.jar and to other jars in the lib folder. Then I execute cobertura-instrument.sh.
But on executing I get the error loaded information on 0 classes . I m giving the complete path to the compiled classes still it is unable to instrument the classes .
So, what am I missing or what could be the possible reasons for this.
Do you mean the error is during the instrumentation, or that after running your tests, the coverage still shows zero?
Here's an example of instrumentation (with Ant):
<target name="--coverage.instrument">
<delete file="cobertura.ser"/>
<mkdir dir="${coverage.instrumented.dir}"/>
<cobertura-instrument todir="${coverage.instrumented.dir}">
<fileset dir="${classes.main.dir}">
<include name="**/*.class"/>
<exclude name="**/*Test.class"/>
</fileset>
</cobertura-instrument>
</target>
Don't forget that you need this sysproperty when testing (eg in Ant Junit task):
<sysproperty key="net.sourceforge.cobertura.datafile" file="cobertura.ser"/>
Once Cobertura is set up an instrumentation has happened, an example of execution:
<target name="--test.unit">
<mkdir dir="${temp.dir}/unit-tests"/>
<junit forkmode="perBatch" printsummary="yes" haltonfailure="no" haltonerror="no"
failureproperty="unit.tests.failed">
<sysproperty key="net.sourceforge.cobertura.datafile" file="cobertura.ser"/>
<classpath refid="classpath.test.utest"/>
<formatter type="xml"/>
<batchtest fork="yes" todir="${temp.dir}/unit-tests">
<fileset dir="${java.src.utest.dir}" includes="**/*Test.java"/>
</batchtest>
</junit>
</target>
I believe that recent versions of Cobertura don't work well with JDK5. Strongly suggest upgrading the JDK.

Ant and Junit test java.lang.ClassNotFoundException

I'm having a problem with an ant build.xml file that's giving me a java.lang.ClassNotFoundExcpetion. I am able to run it fine on windows, but when I port it over to a Linux vm, I get the exception.
<project name="OBJECT" default="compile" >
<!--
Properties for the directories of .java
These are the locations the the source files
These are the locations of the .jar dependancy
-->
<property name="src.dir" location="src"/>
<property name="src.java.dir" location="${src.dir}/csc439"/>
<property name="src.test.dir" location="${src.dir}/test"/>
<property name="lib.dir" location="lib"/>
<!--
Properties for the directories of .class
This gets deleted when ant clean is run
-->
<property name="target.dir" location="target"/>
<property name="target.classes.java.dir" location="${target.dir}/classes/java"/>
<property name="target.classes.test.dir" location="${target.dir}/classes/test"/>
<!--Properties for the report directory-->
<property name="target.report.dir" location="${target.dir}/report"/>
<!--
compile.java
Creates a directory for the .class files of the Java files for the file being tested
Compiles the files and places the .class into the java file created
Imports the necissary .jar files from the lib directory
-->
<target name="compile.java">
<mkdir dir="${target.classes.java.dir}"/>
<javac includeantruntime="true" destdir="${target.classes.java.dir}">
<src path="${src.java.dir}"/>
<classpath>
<pathelement location="${target.classes.java.dir}"/>
<pathelement location="${lib.dir}"/>
<fileset dir="${lib.dir}">
<include name="*.jar"/>
</fileset>
</classpath>
</javac>
</target>
<!--
compile.test
Depends on compile.java to complete first
Creates a directory for the .class files of the Test files
Compiles the files and places the .class into the test file created
-->
<target name="compile.test" depends="compile.java">
<mkdir dir="${target.classes.test.dir}"/>
<javac includeantruntime="true" destdir="${target.classes.test.dir}">
<src path="${src.test.dir}"/>
<classpath>
<pathelement location="${target.classes.java.dir}"/>
<pathelement location="${lib.dir}"/>
<fileset dir="${lib.dir}">
<include name="*.jar"/>
</fileset>
</classpath>
</javac>
</target>
<!--
compile
This the the default
Depends on compile.java, and compile.test
-->
<target name="compile" depends="compile.java,compile.test"/>
<!--
test
Depends on compile
Creates the report file
Runs the JUnit test TestCacheSuite in the test file in the test .class directory
-->
<target name="test" depends="compile">
<mkdir dir="${target.report.dir}"/>
<junit printsummary="yes" haltonerror="yes" haltonfailure="yes" fork="yes">
<formatter type="plain" usefile="false"/>
<formatter type="xml"/>
<test name="test.TestMediaPlayer" todir="${target.report.dir}"/>
<classpath>
<pathelement location="${target.classes.java.dir}"/>
<pathelement location="${target.classes.test.dir}"/>
</classpath>
</junit>
</target>
<!--
report
Depends on test
Creates the file for html documents
Depends on Test
Creates a Junit report
-->
<target name="report" depends="test">
<mkdir dir="${target.report.dir}/html"/>
<junitreport todir="${target.report.dir}">
<fileset dir="${target.report.dir}">
<include name="TEST-*.xml"/>
</fileset>
<report todir="${target.report.dir}/html"/>
</junitreport>
</target>
<!--
clean
Deletes the target directory
This file contains all of the .class files
This file contains all of the reports
-->
<target name = "clean">
<delete dir = "${target.dir}"/>
</target>
This is the error I get while running it on linux.
[junit] Running test.MockObject
[junit] Testsuite: test.MockObject
[junit] Tests run: 1, Failures: 0, Errors: 1, Time elapsed: 0 sec
[junit] Tests run: 1, Failures: 0, Errors: 1, Time elapsed: 0 sec
[junit]
[junit] Caused an ERROR
[junit] test.MockObject
[junit] java.lang.ClassNotFoundException: test.MockObject
[junit] at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
[junit] at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
[junit] at java.security.AccessController.doPrivileged(Native Method)
[junit] at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
[junit] at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
[junit] at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
[junit] at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
[junit] at java.lang.Class.forName0(Native Method)
[junit] at java.lang.Class.forName(Class.java:264)
[junit]
With the exact same build file I get it to run fine on windows.
I have run it fine from command line on windows, as well as using Eclipse both work flawlessly.
From everything that I read it says that I need to check the CLASSPATH, and PATH variables.
I have done this, but I must be doing something wrong. I don't understand why with the same build information that it would run in one os, and not the other.
Any help would be greatly appreciated
There isn't tons and tons of information to go on, but to hazard a guess, I'd say you needed to add your ${lib.dir} as a <pathelement> in the classpath of your junit invocation.
<echoproperties/>
This is your best friend in such cases. If it runs fine on windows, but does not run on linux, then it must be some problem with the file separator (however with ant / works on windows as well), or simply you have different current directory (you use relative path everywhere). If you have different current directory, you will see it in the output of echoproperties as "user.dir". You can set also the javac to verbose="true" and then you can compare the windows output of the script with the linux output.
For me this issue arose because my test file used an inner class. this is compiled as a seperate classfile in eclipse and you will need both classname.class and classname$1.class for ant to run your test.
Try adding junit jar location to your classpath element. I don't know how but that worked for me!
Something like,
<classpath>
<pathelement location="path/to/junit/jar"/>
<pathelement location="${target.classes.java.dir}"/>
<pathelement location="${target.classes.test.dir}"/>
</classpath>

New to Ant, ClassNotFoundException with JUnit

I've been scratching my head over this for a while now (Googled a bunch, looked through other related SO posts to no avail). I have a Java program comprised of two files, Logic and Tests. Tests contains about a hundred JUnit tests, and I've gotten 100% success rate with said tests by calling javac *.java followed by java org.junit.runner.JUnitCore Tests. However when I run my build.xml with a simple ant -verbose test (in order to follow the output since I'm new to all this), I get the following output:
[junit] Testsuite: Tests
[junit] Tests run: 1, Failures: 0, Errors: 1, Time elapsed: 0 sec
[junit]
[junit] Null Test: Caused an ERROR
[junit] Tests
[junit] java.lang.ClassNotFoundException: Tests
[junit] at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
[junit] at java.lang.Class.forName0(Native Method)
[junit] at java.lang.Class.forName(Class.java:247)
[junit]
[junit]
[junit] Test Tests FAILED
BUILD SUCCESSFUL
My build.xml is as follows:
<project name="ETL_Automation" default="test" basedir=".">
<path id="classpath.base">
</path>
<path id="classpath.test">
<pathelement location="${basedir}/mysql-connector-java-5.1.18-bin.jar" />
<pathelement location="${basedir}/junit-4.10.jar"/>
<path refid="classpath.base" />
</path>
<target name="compile">
<javac srcdir="${basedir}">
<classpath refid="classpath.test"/>
</javac>
</target>
<target name="test" depends="compile">
<junit fork="no">
<classpath refid="classpath.test" />
<formatter type="brief" usefile="false" />
<batchtest>
<fileset dir="${basedir}/" includes="Tests.class" />
</batchtest>
</junit>
</target>
<target name="clean" depends="test">
<delete>
<fileset dir="${basedir}" includes="*.class"/>
</delete>
</target>
The directory structure is pretty straightforward. Tests.java, Logic.java, junit-4.10.jar, mysql-connector-java-5.1.18-bin.jar, build.xml, and a referenced .properties file are all in the same folder. The java code references external files but those are unrelated to this particular issue. I don't know if the classpath could be the cause of this issue (as I'm pretty convinced what I currently have doesn't work).
Thanks!
You will need to add the directory with the Tests.class to the classpath.tests classpath (which is ${basedir} in your setup)
Try:
<path id="classpath.test">
<pathelement location="${basedir}/mysql-connector-java-5.1.18-bin.jar" />
<pathelement location="${basedir}/junit-4.10.jar"/>
<pathelement location="${basedir}" />
<path refid="classpath.base" />
</path>

Getting java.lang.NullPointerException errors while running testng.xml

I am getting following error while running testng.xml
ant run_testNG
Buildfile: build.xml
run_testNG:
[testng] Exception in thread "main" java.lang.NullPointerException
[testng] at org.testng.xml.Parser.parse(Parser.java:189)
[testng] at org.testng.TestNG.initializeSuitesAndJarFile(TestNG.java:351)
[testng] at org.testng.TestNG.run(TestNG.java:854)
[testng] at org.testng.TestNG.privateMain(TestNG.java:1178)
[testng] at org.testng.TestNG.main(TestNG.java:1137)
[testng] The tests failed.
BUILD SUCCESSFUL
Total time: 0 seconds
my testng.xml looks like :
<project default="test">
<path id="cp">
<pathelement location="/softwares/lib/testng-5.13.1.jar"/>
<pathelement location="/softwares/cruisecontrol-bin-2.8.3/run_test/run_test/build"/>
</path>
<taskdef name="testng" classpathref="cp" classname="org.testng.TestNGAntTask" />
<target name="test">
<testng classpathref="cp" groups="fast">
<classfileset dir="build" includes="com/example/test/*.class"/>
</testng>
</target>
</project>
my build.xml file is:
<property name="lib.dir" value="/softwares/lib"/>
<property name="src.dir" value="/softwares/src" />
<target name="run_testNG" description="Run TestNG">
<testng classpathref="compile.classpath" haltOnfailure="false">
<xmlfileset dir="/softwares/cruisecontrol-bin-2.8.3/run_test/" includes="testng.xml" />
</testng>
</target>
<target name="start-server">
<java jar="/softwares/selenium/selenium-server-standalone-2.0b2.jar" fork="true" spawn="true">
<arg line="-timeout 30"/>
<jvmarg value="-Dhttp.proxyHost=proxy.corporate.com"/>
<jvmarg value="-Dhttp.proxyPort=3128"/>
</java>
</target>
<target name="stop-server">
<get taskname="selenium-shutdown"
src="http://localhost:4444/selenium-server/driver/?cmd=shutDownSeleniumServer"
dest="result.txt" ignoreerrors="true" />
<echo taskname="selenium-shutdown" message="DGF Errors during shutdown are expected" />
</target>
<taskdef resource="testngtasks" classpath="/softwares/selenium/selenium-2.0b2/libs/testng-5.14.1.jar" />
Can anybody help me...why I am getting this error. Your help will be appreciated
It looks like the problem is that that is not a testng XML file. It is an Ant build file.
For information on what a real testng XML file should look like, see here. Alternatively, rename the file and use Ant to run the tests; e.g.
$ mv testng.xml build.xml
$ ant test
However, it is curious that testng fails in such a spectacular way when you give it an XML file that it doesn't grok. Insufficient testing perhaps? :-)
Looks to me as if it can't find your testng.xml file. That's one possible reason for a NPE thrown by a XML Parser: a null input. I see that ant defaults to build.xml. Do you have a build.xml file too?
Look at the run_testNG script, may be you can/have to add a full path to your file.
I had the same problem,
but found I was picking up a dbUnit dataset file that happend to match the pattern I had set up to pick up testng xml file (*test.xml).
Simply renamed the file and I was good to go.
Hope this helps, even though the question was asked a while ago.

Categories