Tuesday 10 September 2019

Penalty shootout rules

The team kicking first in penalty shootout enjoys a huge advantage, winning roughly 60% of the time. A recent paper (A comparison of penalty shootout designs in soccer) discusses and compares the three rules devised to overcome the unfairness, which are:
  1. Alternating (ABBA) Rule: the order of the first two penalties (AB) is the mirror image of the next two (BA), and this sequence is continued even in the possible sudden death stage of penalty shootouts (the sixth round of penalties is started by team B, the seventh by team A, and so on).
  2. Catch-Up Rule: the order of the penalties in a given round (including the sudden death) is the mirror image of the previous round except if the first team failed and the second scored in the previous round when the order of the teams remains unchanged.
  3. Adjusted Catch-Up Rule: the first five rounds of penalties, started by team A, are kicked according to the Catch-Up Rule, but team B is the first kicker in the sudden death (sixth round) such that the first mover is alternated in this stage.



Let's skip the mathematical computation and jump directly to the conclusion: the Catch up rule does not outperform the Alternating rule while the Adjusted Catch-up rule is fairer than both the Catch up rule and the Alternating rule.

I wrote a python program (https://github.com/sunmingtao/penalty-shootout) to simulate these rules. The assumption is the first kicker has a success rate of 3/4 while the second kicker has a success rate of 2/3. 

The result of the Alternating rule and Catch up rule is very close to that on the paper. However, my program indicates the gap of the result between the Adjusted Catch-up rule and the other two rules is not as large as the paper claims. In fact, the result seems too close to reveal any statistical significance.



In the Euro 2008 quarter final of Italy against Spain, Buffon won the coin toss and yet chose to kick second. We all know the sad outcome for Italy. I wonder if Buffon was too just proud to go first.

Monday 9 September 2019

Python Sonarqube with unit test coverage

Prerequisite

Sonarqube is installed (Create a new project named 'shootout')
Sonar scanner is installed
Anaconda is installed

Install nose and coverage

pip install nose
pip install coverage

Python Project structure

main.py
sample/
    hello.py
testcase/
    test.py

Sonar property file

create sonar-project.properties under the root folder

Contents of the file

sonar.projectKey=shootout #corresponds to the project name in Sonarqube
sonar.projectName=Penalty shootout
sonar.projectVersion=1.0
sonar.language=py
sonar.python.xunit.reportPath=nosetests.xml
sonar.python.coverage.reportPaths=coverage.xml
sonar.python.xunit.skipDetails=true
sonar.coverage.exclusions=testcase/**/* 

Activate the Anaconda virtual environment

Note: It appears that nose can only run in the virtual environment, hence the need of the activation.

conda init

Generate nosetests.xml and coverage.xml

coverage erase 
nosetests --with-coverage --with-xunit
coverage xml

Analyze the project

sonar-scanner

View the result

http://localhost:9000

Troubleshooting

https://github.com/sunmingtao/sample-code/issues/11
https://github.com/sunmingtao/sample-code/issues/12
https://github.com/sunmingtao/sample-code/issues/13
https://github.com/sunmingtao/sample-code/issues/14
https://github.com/sunmingtao/sample-code/issues/15
https://github.com/sunmingtao/sample-code/issues/16

Sunday 1 September 2019

JMockit incompatible with JDK 10/11

Running Unit tests under JDK 10/11 produces error below. It was OK under JDK 8.

[ERROR] Errors: 
[ERROR]   BusinessRepositoryTest>AbstractRepositoryTest.setUp:23 » NoClassDefFound Could...
[ERROR]   BusinessRepositoryTest>AbstractRepositoryTest.setUp:23 » ExceptionInInitializer
[ERROR]   BusinessUserRepositoryTest>AbstractRepositoryTest.setUp:23 » NoClassDefFound C...
[ERROR]   BusinessUserRepositoryTest>AbstractRepositoryTest.setUp:23 » NoClassDefFound C...
[ERROR]   TransactionRepositoryTest>AbstractRepositoryTest.setUp:23 » NoClassDefFound Co...
[ERROR]   TransactionRepositoryTest>AbstractRepositoryTest.setUp:23 » NoClassDefFound Co...
[ERROR]   TransactionRepositoryTest>AbstractRepositoryTest.setUp:23 » NoClassDefFound Co...
[ERROR]   BusinessCustomerLinkServiceTest>AbstractRepositoryTest.setUp:23 » NoClassDefFound
[ERROR]   BusinessServiceTest.save:30 » NullPointer
[INFO] 
[ERROR] Tests run: 9, Failures: 0, Errors: 9, Skipped: 0

The contents of AbstractRepositoryTest is nothing but a Mockup of a public static method

@BeforeEach
void setUp() throws Exception {
    new MockUp<CafamilyUtils>() {
        @mockit.Mock
        public String getLoggedInUserID() {
            return "system";
        }
    };
}

The version of jmockit is too old (1.24) in pom.xml. It needs to be upgraded to a much newer version (1.47). Besides JMockit also requires the -javaagent JVM initialization parameter to be used, according to
https://jmockit.github.io/tutorial/Introduction.html#runningTests

<dependencies>
   <dependency>
      <groupId>org.jmockit</groupId>
      <artifactId>jmockit</artifactId>
      <version>${jmockit.version}</version>
      <scope>test</scope>
   </dependency>
</dependencies>


<plugins>
   <plugin>
      <artifactId>maven-surefire-plugin</artifactId>
      <version>2.22.2</version> <!-- or some other version -->
      <configuration>
         <argLine>
            -javaagent:${settings.localRepository}/org/jmockit/jmockit/${jmockit.version}/jmockit-${jmockit.version}.jar
         </argLine>
      </configuration>
   </plugin>
</plugins>