Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Frequent 'No tests found' when sharding tests #973

Open
chrisbanes opened this issue Jun 4, 2021 · 5 comments
Open

Frequent 'No tests found' when sharding tests #973

chrisbanes opened this issue Jun 4, 2021 · 5 comments
Labels

Comments

@chrisbanes
Copy link

Description

When sharding tests I get frequent (and seemingly random) 'No tests found' errors which fail the build.

See this PR: google/accompanist#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

com.android.build.gradle.internal.testing.ConnectedDevice > No tests found.[test(AVD) - 5.1.1] FAILED 
No tests found. This usually means that your test classes are not in the form that your test runner expects (e.g. don't inherit from TestCase or lack @Test annotations).

AndroidX Test and Android OS Versions

1.3.0

Link to a public git repo demonstrating the problem:

google/accompanist#463

@chrisbanes
Copy link
Author

#264 looks similar

@brettchabot
Copy link
Collaborator

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...

@TWiStErRob
Copy link
Contributor

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.

@Apetree100122
Copy link

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
&

@Apetree100122
Copy link

$variable_name = value;

Loops

  1. IF Family:
    If, If-else, Nested-Ifs are used when you want to perform a certain set of operations based on conditional expressions.

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
}
2. Switch:
Switch is used to execute one set of statement from multiple conditions.

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;
}

  1. For:
    For loop is used to iterate a set of statements based on a condition.

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
}
4. While:
While is also used to iterate a set of statements based on a condition. Usually while is preferred when number of iterations are not known in advance.

while(condition) {
// code
}
5. Do-While:
Do-while is also used to iterate a set of statements based on a condition. It is mostly used when you need to execute the statements atleast once.

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants