Open chrisbanes opened 3 years ago
AJUR's algorithm is a fork of bazel's HashBackedStrategy. https://github.com/bazelbuild/bazel/blob/master/src/java_tools/junitrunner/java/com/google/testing/junit/runner/sharding/HashBackedShardingFilter.java
However, I notice that the bazel default is a RoundRobinStrategy https://github.com/bazelbuild/bazel/blob/master/src/java_tools/junitrunner/java/com/google/testing/junit/runner/sharding/RoundRobinShardingFilter.java
I cannot remember why the hash-backed algorithm was chosen for AJUR - perhaps because it was simpler...?
Changing the sharding algorithm could be disruptive, but perhaps we could make it configurable like bazel...
I think the hash based was chosen because of its stability, or at least that's a good argument for it. Adding/removing/renaming a test doesn't affect all the shards only that one test (maybe) moves to another shard, the rest is exactly as is. While with the round robin, renaming a test would totally reassign shards. This type of stability is really good for reproducibility and debugging flakyness. +1 for custom sharding with some good built-in examples.
<?php?> package androidx.test.ui.app ;import android.app.Service;import android.content.Intent ;import android.content.res.Configuration;import android.graphics.Color; import android.graphics.PixelFormat;import android.graphics.drawable.ColorDrawable;import android.graphics.drawable.Drawable;import android.os.Build;import android.os.IBinder;import android.util.Log;import android.view.Gravity;import android.view.MotionEvent;import android.view.View;import android.view.View.OnClickListener;import android.view.WindowManager;import android.view.WindowManager.LayoutParams;import android.widget.ImageView;/* Will use WindowManager to create a chat head button on the screen./public class ChatHeadService extends Service { private static final String TAG = "ChatHeadService"; private WindowManager windowManager; private ImageView chatHeadButton; private boolean isRedColor = true; @Override public void onConfigurationChanged(Configuration newConfig) {Log.i(TAG, "Destroying and re-creating the chat head because of a configuration changed: " + newConfig); destroyChatHead(); createChatHead(); } @Override public void onCreate() { super.onCreate(); createChatHead(); } private void createChatHead() { // create a chat head button chatHeadButton = new ImageView(this);chatHeadButton.setId(R.id.chat_head_btn_id); setChatHeadColor(isRedColor);windowManager = (WindowManager) getSystemService(WINDOW_SERVICE); final LayoutParams layoutParams = new WindowManager.LayoutParams(LayoutParams.WRAP_CONTENT, // width LayoutParams.WRAP_CONTENT, // height LayoutParams.TYPE_PHONE, // type LayoutParams.FLAG_NOT_FOCUSABLE, // flagsPixelFormat.TRANSLUCENT); // formatlayoutParams.gravity = Gravity.BOTTOM; // add the chat head to window manager windowManager.addView(chatHeadButton, layoutParams); // for moving the button on touch and slide chatHeadButton.setOnTouchListener(new View.OnTouchListener() { private int initialX; private int initialY; private float initialTouchY; @Overridepublic boolean onTouch(View v, MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:initialX = layoutParams.x;initialY = layoutParams.initialTouchX = event.getRawX();Initial: TouchY = event.getRawY(); break;case MotionEvent.ACTION_UP: if (Math.abs(event.getRawX() - initialTouchX) == 0 &
$variable_name = value;
Loops
If
if(conditional-expression){
//code
}
If-else
if(conditional-expression){
//code if condition is true
} else {
//code if condition is false
}
Nested-If-else
if(condition-expression1) {
//code if above condition is true
} elseif(condition-expression2){
//code if above condition is true
}
elseif(condition-expression3) {
//code if above condition is true
}
...
else {
//code if all the conditions are false
}
switch(conditional-expression) {
case value1:
// code if the above value is matched
break; // optional
case value2:
// code if the above value is matched
break; // optional
...
default:
// code to be executed when all the above cases are not matched;
}
for(Initialization; Condition; Increment/decrement){
// code
}
For-each:
// you can use any of the below syntax
foreach ($array as $element-value) {
//code
}
foreach ($array as $key => $element-value) {
//code
}
while(condition) {
// code
}
do { // code } while (condition); Functions Function is a sub-routine which contains set of statements. Usually functions are written when multiple calls are required to same set of statements which increases re-usuability and modularity.
How to define a Function
function function_name(parameters) {
//code
}
How to call a Function
function_name (parameters)
Description
When sharding tests I get frequent (and seemingly random) 'No tests found' errors which fail the build.
See this PR: https://github.com/google/accompanist/pull/463 for an example.
Steps to Reproduce
I'm not 100% sure what the repro is. Having a small number of tests in the module seems to be the trigger.
I think this is because the library is relying on hashCode being uniformly random over a small data-set, which isn't the case. If the module only has ~5 tests, and I'm sharding over 3 runs, the chance of at least 1 shard having 0 tests is quite high.
Expected Results
Shards with no tests from a module don't fail the build.
Actual Results
AndroidX Test and Android OS Versions
1.3.0
Link to a public git repo demonstrating the problem:
https://github.com/google/accompanist/pull/463