Dzisiaj zajmiemy się przetestowaniem naszego algorytmu do wykrywania plagiatów w praktyce. Miejmy nadzieję, że to chociaż trochę zadziała 😉

Zestaw testowy

Musimy najpierw zacząć od przygotowania danych, na których będziemy testować naszą aplikacje.

Dzisiaj przygotujemy specjalnie spreparowane kody źródłowe i zobaczymy jak z nimi poradzi sobie nasza aplikacja.

Zacznijmy od stworzenia naszej wyjściowej klasy, która będzie zawierała kilka pól oraz metod.

public class ManyMethods {
	private int a;
	private int b;
	private double c;
	private double d;
	private String e;
	private String f;
	
	public ManyMethods() {
		a = 4;
		b = 1;
		c = -1.43;
		d = 9.12;
		e = "test";
		f = "asdasd";
	}
	
	public void firstMethod() {
		a = a + b;		
	}
	
	public void secondMethod() {
		a = a* a* a* a* a *a / 2*a;
		b = (int) (b + a -8 + 9 * c);
	}
	
	public void thridMethod() {
		c *= 123.432;
		e += "Asddsa";
	}
	
	public void fourthMethod() {
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.append("raz");
		stringBuilder.append(f);
		stringBuilder.append("dwa");
		f = stringBuilder.toString();
	}
	
	public void fifthMethod() {
		e = e + "asd " + a + b+ c+ d+ e+ f;
	}
	
	public static void main(String[] args) {
		ManyMethods manyMethods = new ManyMethods();
		manyMethods.firstMethod();
		manyMethods.secondMethod();
		manyMethods.thridMethod();
		manyMethods.fourthMethod();
		manyMethods.fifthMethod();
	}
}

Będzie to nasz wyjściowy kod, od którego będziemy robić wszystkie modyfikacje.

Wykonajmy następujące modyfikacje kodu:

  1. Zmieńmy nazwy metod i pól
    public class ManyMethodsDifferentNames {
    	private int firstIntegerVariable;
    	private int secondIntegerVariable;
    	private double firstDoubleVariable;
    	private double secondDoubleVariable;
    	private String firstStringVariable;
    	private String secondStringVariable;
    	
    	public ManyMethodsDifferentNames() {
    		firstIntegerVariable = 4;
    		secondIntegerVariable = 1;
    		firstDoubleVariable = -1.43;
    		secondDoubleVariable = 9.12;
    		firstStringVariable = "test";
    		secondStringVariable = "asdasd";
    	}
    	
    	public void doSomeActionOne() {
    		firstIntegerVariable = firstIntegerVariable + secondIntegerVariable;		
    	}
    	
    	public void doSomeActionTwo() {
    		firstIntegerVariable = firstIntegerVariable* firstIntegerVariable* firstIntegerVariable* firstIntegerVariable* firstIntegerVariable *firstIntegerVariable / 2*firstIntegerVariable;
    		secondIntegerVariable = (int) (secondIntegerVariable + firstIntegerVariable -8 + 9 * firstDoubleVariable);
    	}
    	
    	public void doSomeActionThird() {
    		firstDoubleVariable *= 123.432;
    		firstStringVariable += "Asddsa";
    	}
    	
    	public void doSomeActionFour() {
    		StringBuilder stringBuilder = new StringBuilder();
    		stringBuilder.append("raz");
    		stringBuilder.append(secondStringVariable);
    		stringBuilder.append("dwa");
    		secondStringVariable = stringBuilder.toString();
    	}
    	
    	public void doSomeActionFive() {
    		firstStringVariable = firstStringVariable + "asd " + firstIntegerVariable + secondIntegerVariable+ firstDoubleVariable+ secondDoubleVariable+ firstStringVariable+ secondStringVariable;
    	}
    	
    	public static void main(String[] args) {
    		ManyMethodsDifferentNames manyMethods = new ManyMethodsDifferentNames();
    		manyMethods.doSomeActionOne();
    		manyMethods.doSomeActionTwo();
    		manyMethods.doSomeActionThird();
    		manyMethods.doSomeActionFour();
    		manyMethods.doSomeActionFive();
    	}
    }
  2. Przenieśmy operacje z metod do metody main, a same metody usuńmy
    public class ManyMethodsIncluded {
    	private static int a;
    	private static int b;
    	private static double c;
    	private static double d;
    	private static String e;
    	private static String f;
    	
    	public static void main(String[] args) {
    		a = 4;
    		b = 1;
    		c = -1.43;
    		d = 9.12;
    		e = "test";
    		f = "asdasd";
    		a = a + b;		
    		a = a* a* a* a* a *a / 2*a;
    		b = (int) (b + a -8 + 9 * c);
    		c *= 123.432;
    		e += "Asddsa";
    		StringBuilder stringBuilder = new StringBuilder();
    		stringBuilder.append("raz");
    		stringBuilder.append(f);
    		stringBuilder.append("dwa");
    		f = stringBuilder.toString();
    		e = e + "asd " + a + b+ c+ d+ e+ f;
    	}
    }
  3. Dodajmy kilka nieużywanych metod
    public class ManyMethodsUnnecessaryStaff {
    	private int a;
    	private int b;
    	private double c;
    	private double d;
    	private String e;
    	private String f;
    	
    	public void stupidMethodOne() {
    		f = "asd sa dsa" + " dsad sad sa" + f + "dsa dsa ";
    		e += "dsadsa dsa dsa + dsad sa " + " dsad sadsa";
    	}
    	
    	public ManyMethodsUnnecessaryStaff() {
    		a = 4;
    		b = 1;
    		c = -1.43;
    		d = 9.12;
    		e = "test";
    		f = "asdasd";
    	}
    	
    	public void stupidMethodTwo() {
    		a = Math.abs(-123213213*3*5);
    		b += 3* b + a + c;
    	}
    	
    	public void firstMethod() {
    		a = a + b;		
    	}
    	
    	public void stupidMethodThree() {
    		c = 123.543534/42.543*423.123+65.2;
    		d = Math.sqrt(c + d);
    	}
    	
    	public void secondMethod() {
    		a = a* a* a* a* a *a / 2*a;
    		b = (int) (b + a -8 + 9 * c);
    	}
    	
    	public void stupidMethodFour() {
    		e = "olelele";
    		try {
    			Thread.sleep(200);
    		}
    		catch(Exception e) {
    			f = "hahah";
    		}
    	}
    	
    	public void thridMethod() {
    		c *= 123.432;
    		e += "Asddsa";
    	}
    	
    	public void stupidMethodFive() {
    		c = 123.543534/42.543*423.123+65.2;
    		d = Math.sqrt(c + d);
    	}
    	
    	public void fourthMethod() {
    		StringBuilder stringBuilder = new StringBuilder();
    		stringBuilder.append("raz");
    		stringBuilder.append(f);
    		stringBuilder.append("dwa");
    		f = stringBuilder.toString();
    	}
    	
    	public void stupidMethodSix() {
    		e = "olelele";
    		try {
    			Thread.sleep(200);
    		}
    		catch(Exception e) {
    			f = "hahah";
    		}
    	}
    	
    	public void fifthMethod() {
    		e = e + "asd " + a + b+ c+ d+ e+ f;
    	}
    	
    	public static void main(String[] args) {
    		ManyMethodsUnnecessaryStaff manyMethods = new ManyMethodsUnnecessaryStaff();
    		manyMethods.firstMethod();
    		manyMethods.secondMethod();
    		manyMethods.thridMethod();
    		manyMethods.fourthMethod();
    		manyMethods.fifthMethod();
    	}
    }
  4. Czwartym kodem do porównania, będzie kopia oryginalnego kodu

Testujemy!

Stwórzmy prostą metodę, która wczyta nasze kody źródłowe oraz wywoła naszą aplikacje i wyliczy prawdopodobieństwo.

fun main(args: Array<String>) {
    val root = "C:\\kody_do_testow\\";
    val oryg = FileInputStream(root + "oryg\\ManyMethods.java").bufferedReader().use { it -> it.readText() }
    val code1 = FileInputStream(root + "diff_names\\ManyMethodsDifferentNames.java").bufferedReader().use { it -> it.readText() }
    val code2 = FileInputStream(root + "included\\ManyMethodsIncluded.java").bufferedReader().use { it -> it.readText() }
    val code3 = FileInputStream(root + "unn_staff\\ManyMethodsUnnecessaryStaff.java").bufferedReader().use { it -> it.readText() }
    val code4 = FileInputStream(root + "oryg\\ManyMethods.java").bufferedReader().use { it -> it.readText() }

    var calculate = PlagDetectorAlgorithm().calculate(LexerType.JAVA_8, oryg, code1)
    println(String.format("code1 d=%.2f, px=%.2f, py=%.2f", calculate.d, calculate.px, calculate.py))

    calculate = PlagDetectorAlgorithm().calculate(LexerType.JAVA_8, oryg, code2)
    println(String.format("code2 d=%.2f, px=%.2f, py=%.2f", calculate.d, calculate.px, calculate.py))

    calculate = PlagDetectorAlgorithm().calculate(LexerType.JAVA_8, oryg, code3)
    println(String.format("code3 d=%.2f, px=%.2f, py=%.2f", calculate.d, calculate.px, calculate.py))

    calculate = PlagDetectorAlgorithm().calculate(LexerType.JAVA_8, oryg, code4)
    println(String.format("code4 d=%.2f, px=%.2f, py=%.2f", calculate.d, calculate.px, calculate.py))
}

Omówmy wyniki!

Pora na krótką analizę wyników.

  1. Zmienione nazwy metod i pól
    Program zwrócił następujący wynik:

    d=0.97, px=0.98, py=0.98

    Tutaj możemy zauważyć, że program poradził sobie całkiem nieźle. Wskaźniki są bardzo bliskie 1, więc możemy z dużą dozą prawdopodobieństwa stwierdzić, że porównywane programy zawierają ten sam kod.

  2. Kod z metod przeniesiony do głównej metody
    Zwrócony wynik:

    d=0.57, px=0.69, py=0.76

    Tutaj niestety algorytm poradził sobie dużo gorzej. Wystarczyła drobna zmiana w kodzie i stopień wykrywalności jest o wiele mniejszy. Może to byś spowodowane tym, że testowany kod jest krótki i nawet takie małe zmiany znacząco wpływają na wyniki. Warto na pewno się temu przyjrzeć w przyszłości.

  3. Kod z nieużywanymi metodami
    Wyliczone podobieństwo:

    d=0.63, px=0.92, py=0.67

    Na tym przykładzie możemy zobaczyć w praktyce wykorzystanie wskaźników px oraz py. O ile główny wskaźnik wynosi tylko 0.63 i nie musi wydawać się podejrzany, o tyle wartość wskaźnika px (0.92) mocno wskazuje, że jeden z porównywanych kodów zawiera dużą część drugiego kodu, oraz że mógł zajść plagiat.

  4. Porównanie dwóch identycznych kodów źródłowych
    Wynik:

    d=0.97, px=0.98, py=0.98

    Tutaj podobnie jak w pierwszym przypadku algorytm poradził sobie bardzo dobrze i wyliczył wskaźniki bliskie 1.

Podsumowanie

Dzisiaj wykonaliśmy pierwsze testy naszego algorytmu. Do testów użyliśmy kilka spreparowanych kodów źródłowych. Pierwsze wyniki wskazują, że algorytm jakieś prawdopodobieństwo wylicza 😉

W następnym wpisie odnośnie PlagDetectora przeprowadzimy testy na kodach źródłowych z28za czasów studenckich 😉