name: run and save test times when cache present
description: run and save test times when cache present
runs:
  using: "composite"

  steps:

    - name: download tests with times
      if: env.BASE_BRANCH != '2.1.x'
      uses: actions/download-artifact@v3
      with:
        name: tests-with-times-sorted.txt
        path: /tmp/

    - name: download tests without times
      if: env.BASE_BRANCH != '2.1.x'
      uses: actions/download-artifact@v3
      with:
        name: tests-without-times.txt
        path: /tmp/

    - name: split tests into known times and un-known times
      shell: bash
      run: |
        
        echo "------------------------------------------------------------------------------"
        cat /tmp/tests-with-times-sorted.txt
        echo "------------------------------------------------------------------------------"
        if [[ -f /tmp/tests-without-times.txt ]]; then cat /tmp/tests-without-times.txt; fi
        echo "------------------------------------------------------------------------------"
        
        ############################################################################################################
        ############################################################################################################
        ############################################################################################################
        # For each number_of_instances, iterate from bottom to top ('for ((j=$number_of_lines_in_file; j>0; j--))')
        # (starting from the test that has the max time). Easier to understand is via an example. 
        # Suppose our /tmp/sorted.txt looks like this (very artificial):
        # testA  1 sec
        # testB  2 sec
        # testC  3 sec
        
        # average_time_per_instance = 3.1 sec
        # number_of_lines_in_file   = 3
        
        # we start iterating from bottom to top and first get 'testC' that has a running time of 3 sec
        # since 3 sec < 3.1 sec ('if [[ $next_sum -lt $average_time_per_instance ]];')
        # this test needs to be taken in 'tests_to_take_in_current_iteration', thus:
        # tests_to_take_in_current_iteration=testC; also next_sum becomes 3sec
        # we then drop this line from /tmp/sorted.txt because we have already processed it 
        # ('sed -i "" "${j}d" /tmp/tests-with-times-sorted.txt')
        # we also decrement j, since we removed one line from the file

        # we then take testB, add its time to next_sum, thus next_sum = 5 sec, but now the time is NOT < 3.1 sec
        # This means we do not take testB in current iteration and skip it.
        
        # Same goes for testA, next_sum = 4 sec, and it is NOT < 3.1 sec
        # There are no more lines in file to iterate, thus tests_to_take_in_current_iteration=testC
        
        # we repeat the process again and now tests_to_take_in_current_iteration=testB,testA because their cumulative 
        # sum will be 3 sec and it's < 3.1 sec.
  
        number_of_instances=${NUMBER_OF_JOBS}
        echo "number of instances $number_of_instances"
  
        average_time_per_instance=${AVERAGE_TIME_PER_INSTANCE}
        echo "average time per instance $average_time_per_instance"
  
        number_of_lines_in_file=$(grep -c ^ /tmp/tests-with-times-sorted.txt)
        echo "number of lines in fine : $number_of_lines_in_file"
        tests_to_run_in_current_index=''
  
        for ((i=0; i<=${number_of_instances}; i++)) ; do
  
            sum=0
            tests_to_take_in_current_iteration=''
            for ((j=$number_of_lines_in_file; j>0; j--)) ; do
  
              current_line_in_file=$(awk "NR == ${j}" /tmp/tests-with-times-sorted.txt)
              current_test_time=$(echo $current_line_in_file | awk '{print $2}')
              current_test_time=$(printf "%.0f\n" "$current_test_time")
              current_test_name=$(echo $current_line_in_file | awk '{print $1}')
              next_sum=$(( $sum + $current_test_time ))
          
              if [[ $next_sum -lt $average_time_per_instance ]]; then
          
                  sum=$(( $sum + $current_test_time ))
  
                  if [[ -z $tests_to_take_in_current_iteration ]]; then
                      tests_to_take_in_current_iteration="$current_test_name"
                  else
                      tests_to_take_in_current_iteration="$tests_to_take_in_current_iteration,$current_test_name"
                  fi
  
                  sed -i "${j}d" /tmp/tests-with-times-sorted.txt
                  number_of_lines_in_file=$(( $number_of_lines_in_file-1 ))
                  continue
              fi
  
            done
  
            if [[ $i = ${CURRENT_INDEX} ]]; then
              echo "current index : ${CURRENT_INDEX}"
              tests_to_run_in_current_index=$tests_to_take_in_current_iteration
              echo "time of tests in current index : $sum"
              
              # this can be the case when we delete some tests in some PR for example
              # the previous cache will contain more tests then we currently have, so some 
              # matrix instances will have no tests to run
              if [[ "$sum" -eq "0" ]]; then
                echo "no tests (with known times) to run in current index"
                tests_to_run_in_current_index='none'
              fi
              
            fi
            
        done
        
        # here we compute tests that are "new", these are tests that our cache is not aware of
        # for example these can come from a new PR where new tests are added
        # This last step can have already existing tests to run, so we need to be careful to append
        # new tests here. 
        
        if [[ ${CURRENT_INDEX} = ${NUMBER_OF_JOBS} ]]; then
          echo "last index spotted"
          
          if [ ! -f /tmp/tests-without-times.txt ]; then
            echo "no tests outside cache found"
           
            if [ -z "$tests_to_run_in_current_index" ]; then
              echo "no tests to run in the last index"
              tests_to_run_in_current_index='none'      
            fi
        
          else 
            TESTS_WITHOUT_TIMES=($(cat /tmp/tests-without-times.txt))
        
            for test in "${TESTS_WITHOUT_TIMES[@]}"; do
              if [[ -z "$tests_to_run_in_current_index" ]]; then
                tests_to_run_in_current_index="$test"
              else
                tests_to_run_in_current_index="$tests_to_run_in_current_index,$test"
              fi    
            done

          fi        
        fi 
        
        echo "will run tests : $tests_to_run_in_current_index"
        
        ./mvnw -s .settings.xml \
              -DtestsToRun=${tests_to_run_in_current_index} \
              -Dsurefire-reports-directory=surefire-reports/${CURRENT_INDEX} \
              -Dfailsafe-reports-directory=failsafe-reports/${CURRENT_INDEX} \
              -e clean install \
              -P sonar -nsu --batch-mode \
              -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn \
              -Dhttp.keepAlive=false \
              -Dmaven.wagon.http.pool=false \
              -Dmaven.wagon.http.retryHandler.class=standard \
              -Dmaven.wagon.http.retryHandler.count=3 \
              -Dskip.build.image=true
        
        touch /tmp/test_times_${{ env.CURRENT_INDEX }}.txt
        
        # if there are no tests to run in the last index, don't parse anything
        if [ $tests_to_run_in_current_index != "none" ]; then
            IFS=',' read -r -a sliced_array <<< "$tests_to_run_in_current_index"
            unset IFS
        
            for i in "${sliced_array[@]}"; do
              
              # can be present in the last index as 'none,testA,testB'
              if [[ "$i" == "none" ]]; then
                echo "skipping 'none'"
              else
                filename="${i}.txt"
                echo "searching for filename: ${filename}"
                file=$(find . -name "${filename}")
                echo "found file: ${file}"
                result=$(cat "${file}" | grep 'elapsed' | awk '{print $12, $13}')
                echo "run test: ${i} in : ${result}" >> /tmp/test_times_${{ env.CURRENT_INDEX }}.txt
              fi
            done
        fi
        
        

    - name: show individual test times
      shell: bash
      if: env.BASE_BRANCH != '2.1.x'
      run: cat /tmp/test_times_${{ env.CURRENT_INDEX }}.txt

    - name: upload individual tests
      if: env.BASE_BRANCH != '2.1.x'
      uses: actions/upload-artifact@v3
      with:
        name: test_times_${{ env.CURRENT_INDEX }}.txt
        path: /tmp/test_times_${{ env.CURRENT_INDEX }}.txt
