SlideShare ist ein Scribd-Unternehmen logo
1 von 39
Downloaden Sie, um offline zu lesen
The Power of Custom Lint Checks
droidcon Berlin | 05.06.2015 | Marc Prengemann
About me
Marc Prengemann
Working student
Mail: marc@wire.com
Wire: marcprengemann@gmail.com
Github: winterDroid
Google+: Marc Prengemann
Lint? What?
What is Lint?
http://developer.android.com/tools/help/lint.html
When should I use Lint?
• To ensure code quality
• Focus in reviews on real code
• Prevent people from misusing internal
libraries
… but what are the challenges?
• @Beta
• Getting familiar with the Lint API
• Integrating within your Gradle Build
• Debugging / Testing
Getting started with own Checks
Test ideas
• Fragments and Activities should extend your
BaseClass
• Use ViewUtils instead of finding and casting a
View
• Don’t check floats for equality - use
Float.equals instead
• Find leaking resources
• Enforce Naming conventions
• Find hardcoded values in XMLs
A real example
• Timber
• logger by Jake Wharton
• https://github.com/JakeWharton/
timber
• want to create a detector that finds
misuse of android.util.Log instead of
Timber
Detector
• responsible for scanning through code
and to find issues and report them
public class WrongTimberUsageDetector extends Detector
implements Detector.JavaScanner {



public static final Issue ISSUE = ...;



@Override

public List<String> getApplicableMethodNames() {

return Arrays.asList("v", "d", "i", "w", "e", "wtf");

}



@Override

public void visitMethod(@NonNull JavaContext context, 

AstVisitor visitor, 

@NonNull MethodInvocation node) {

if (!(node.astOperand() instanceof VariableReference)) {

return;

}

VariableReference ref = (VariableReference) node.astOperand();

if ("Log".equals(ref.astIdentifier().astValue())) {

context.report(ISSUE,

node,

context.getLocation(node),

"Using 'Log' instead of 'Timber'");

}

}

}
Detector
• responsible for scanning through code
and to find issues and report them
• most detectors implement one or more
scanner interfaces that depend on the
specified scope
• Detector.XmlScanner
• Detector.JavaScanner
• Detector.ClassScanner
public class WrongTimberUsageDetector extends Detector
implements Detector.JavaScanner {



public static final Issue ISSUE = ...;



@Override

public List<String> getApplicableMethodNames() {

return Arrays.asList("v", "d", "i", "w", "e", "wtf");

}



@Override

public void visitMethod(@NonNull JavaContext context, 

AstVisitor visitor, 

@NonNull MethodInvocation node) {

if (!(node.astOperand() instanceof VariableReference)) {

return;

}

VariableReference ref = (VariableReference) node.astOperand();

if ("Log".equals(ref.astIdentifier().astValue())) {

context.report(ISSUE,

node,

context.getLocation(node),

"Using 'Log' instead of 'Timber'");

}

}

}
Detector
• responsible for scanning through code
and finding issue instances and reporting
them
• most detectors implement one or more
scanner interfaces
• can detect multiple different issues
• allows you to have different severities for
different types of issues
public class WrongTimberUsageDetector extends Detector
implements Detector.JavaScanner {



public static final Issue ISSUE = ...;



@Override

public List<String> getApplicableMethodNames() {

return Arrays.asList("v", "d", "i", "w", "e", "wtf");

}



@Override

public void visitMethod(@NonNull JavaContext context, 

AstVisitor visitor, 

@NonNull MethodInvocation node) {

if (!(node.astOperand() instanceof VariableReference)) {

return;

}

VariableReference ref = (VariableReference) node.astOperand();

if ("Log".equals(ref.astIdentifier().astValue())) {

context.report(ISSUE,

node,

context.getLocation(node),

"Using 'Log' instead of 'Timber'");

}

}

}
Detector
• responsible for scanning through code
and finding issue instances and reporting
them
• most detectors implement one or more
scanner interfaces
• can detect multiple different issues
• define the calls that should be analyzed
• overwritten method depends on
implemented scanner interface
• depends on the goal of the detector
public class WrongTimberUsageDetector extends Detector
implements Detector.JavaScanner {



public static final Issue ISSUE = ...;



@Override

public List<String> getApplicableMethodNames() {

return Arrays.asList("v", "d", "i", "w", "e", "wtf");

}



@Override

public void visitMethod(@NonNull JavaContext context, 

AstVisitor visitor, 

@NonNull MethodInvocation node) {

if (!(node.astOperand() instanceof VariableReference)) {

return;

}

VariableReference ref = (VariableReference) node.astOperand();

if ("Log".equals(ref.astIdentifier().astValue())) {

context.report(ISSUE,

node,

context.getLocation(node),

"Using 'Log' instead of 'Timber'");

}

}

}
Detector
• responsible for scanning through code
and finding issue instances and reporting
them
• most detectors implement one or more
scanner interfaces
• can detect multiple different issues
• define the calls that should be analyzed
• analyze the found calls
• overwritten method depends on
implemented scanner interface
• depends on the goal of the detector
public class WrongTimberUsageDetector extends Detector
implements Detector.JavaScanner {



public static final Issue ISSUE = ...;



@Override

public List<String> getApplicableMethodNames() {

return Arrays.asList("v", "d", "i", "w", "e", "wtf");

}



@Override

public void visitMethod(@NonNull JavaContext context, 

AstVisitor visitor, 

@NonNull MethodInvocation node) {

if (!(node.astOperand() instanceof VariableReference)) {

return;

}

VariableReference ref = (VariableReference) node.astOperand();

if ("Log".equals(ref.astIdentifier().astValue())) {

context.report(ISSUE,

node,

context.getLocation(node),

"Using 'Log' instead of 'Timber'");

}

}

}
Detector
• responsible for scanning through code
and finding issue instances and reporting
them
• most detectors implement one or more
scanner interfaces
• can detect multiple different issues
• define the calls that should be analyzed
• analyze the found calls
• report the found issue
• specify the location
• report() will handle to suppress warnings
• add a message for the warning
public class WrongTimberUsageDetector extends Detector
implements Detector.JavaScanner {



public static final Issue ISSUE = ...;



@Override

public List<String> getApplicableMethodNames() {

return Arrays.asList("v", "d", "i", "w", "e", "wtf");

}



@Override

public void visitMethod(@NonNull JavaContext context, 

AstVisitor visitor, 

@NonNull MethodInvocation node) {

if (!(node.astOperand() instanceof VariableReference)) {

return;

}

VariableReference ref = (VariableReference) node.astOperand();

if ("Log".equals(ref.astIdentifier().astValue())) {

context.report(ISSUE,

node,

context.getLocation(node),

"Using 'Log' instead of 'Timber'");

}

}

}
Issue
• potential bug in an Android application
• is discovered by a Detector
• are exposed to the user
public static final Issue ISSUE =
Issue.create("LogNotTimber",

"Used Log instead of Timber",

"Since Timber is included in the project, "
+ "it is likely that calls to Log should "

+ "instead be going to Timber.",

Category.MESSAGES,

5,

Severity.WARNING,

new Implementation(WrongTimberUsageDetector.class,
Scope.JAVA_FILE_SCOPE));
Issue
• the id of the issue
• should be unique
• recommended to add the package name as
a prefix like com.wire.LogNotTimber
public static final Issue ISSUE =
Issue.create(“LogNotTimber",
"Used Log instead of Timber",

"Since Timber is included in the project, “
+ "it is likely that calls to Log should "

+ "instead be going to Timber.",

Category.MESSAGES,

5,

Severity.WARNING,

new Implementation(WrongTimberUsageDetector.class,
Scope.JAVA_FILE_SCOPE));
Issue
• the id of the issue
• short summary
• typically 5-6 words or less
• describe the problem rather than the fix public static final Issue ISSUE =
Issue.create(“LogNotTimber",
"Used Log instead of Timber",

"Since Timber is included in the project, “
+ "it is likely that calls to Log should "

+ "instead be going to Timber.",

Category.MESSAGES,

5,

Severity.WARNING,

new Implementation(WrongTimberUsageDetector.class,
Scope.JAVA_FILE_SCOPE));
Issue
• the id of the issue
• short summary
• full explanation of the issue
• should include a suggestion how to fix it public static final Issue ISSUE =
Issue.create(“LogNotTimber",
"Used Log instead of Timber",

"Since Timber is included in the project, “
+ "it is likely that calls to Log should "

+ "instead be going to Timber.",

Category.MESSAGES,

5,

Severity.WARNING,

new Implementation(WrongTimberUsageDetector.class,
Scope.JAVA_FILE_SCOPE));
Issue
• the id of the issue
• short summary
• full explanation of the issue
• the associated category, if any
• Lint
• Correctness (incl. Messages)
• Security
• Performance
• Usability (incl. Icons, Typography)
• Accessibility
• Internationalization
• Bi-directional text
public static final Issue ISSUE =
Issue.create(“LogNotTimber",
"Used Log instead of Timber",

"Since Timber is included in the project, “
+ "it is likely that calls to Log should "

+ "instead be going to Timber.",

Category.MESSAGES,

5,

Severity.WARNING,

new Implementation(WrongTimberUsageDetector.class,
Scope.JAVA_FILE_SCOPE));
Issue
• the id of the issue
• short summary
• full explanation of the issue
• the associated category, if any
• the priority
• a number from 1 to 10
• 10 being most important/severe
public static final Issue ISSUE =
Issue.create(“LogNotTimber",
"Used Log instead of Timber",

"Since Timber is included in the project, “
+ "it is likely that calls to Log should "

+ "instead be going to Timber.",

Category.MESSAGES,

5,

Severity.WARNING,

new Implementation(WrongTimberUsageDetector.class,
Scope.JAVA_FILE_SCOPE));
Issue
• the id of the issue
• short summary
• full explanation of the issue
• the associated category, if any
• the priority
• the default severity of the issue
• Fatal
• Error
• Warning
• Informational
• Ignore
public static final Issue ISSUE =
Issue.create(“LogNotTimber",
"Used Log instead of Timber",

"Since Timber is included in the project, “
+ "it is likely that calls to Log should "

+ "instead be going to Timber.",

Category.MESSAGES,

5,

Severity.WARNING,

new Implementation(WrongTimberUsageDetector.class,
Scope.JAVA_FILE_SCOPE));
Issue
• the id of the issue
• short summary
• full explanation of the issue
• the associated category, if any
• the priority
• the default severity of the issue
• the default implementation for this issue
• maps to the Detector class
• specifies the scope of the implementation
public static final Issue ISSUE =
Issue.create(“LogNotTimber",
"Used Log instead of Timber",

"Since Timber is included in the project, “
+ "it is likely that calls to Log should "

+ "instead be going to Timber.",

Category.MESSAGES,

5,

Severity.WARNING,

new Implementation(WrongTimberUsageDetector.class,
Scope.JAVA_FILE_SCOPE));
Issue
• the id of the issue
• short summary
• full explanation of the issue
• the associated category, if any
• the priority
• the default severity of the issue
• the default implementation for this issue
• the scope of the implementation
• describes set of files a detector must
consider when performing its analysis
• Include:
• Resource files / folder
• Java files
• Class files
public static final Issue ISSUE =
Issue.create(“LogNotTimber",
"Used Log instead of Timber",

"Since Timber is included in the project, “
+ "it is likely that calls to Log should "

+ "instead be going to Timber.",

Category.MESSAGES,

5,

Severity.WARNING,

new Implementation(WrongTimberUsageDetector.class,
Scope.JAVA_FILE_SCOPE));
Issue Registry
• provide list of checks to be performed
• return a list of Issues in getIssues()
public class CustomIssueRegistry extends IssueRegistry {

@Override

public List<Issue> getIssues() {

return Arrays.asList(WrongTimberUsageDetector.ISSUE);

}

}
Include within your Project
Project structure
• lintrules
• Java module
• source of all custom detectors and our
IssueRegistry
• specify reference in build.gradle as
attribute Lint-Registry
• will export a lint.jar
$ ./gradlew projects
:projects
------------------------------------------------------------
Root project
------------------------------------------------------------
Root project ‘awsm_app'
+--- Project ':app'
+--- Project ':lintlib'
--- Project ':lintrules'
jar {
manifest {
attributes 'Manifest-Version': 1.0
attributes 'Lint-Registry':
'com.checks.CustomIssueRegistry'
}
}
Project structure
• lintrules
• lintlib
• Android library module
• has a dependency to lintrules $ ./gradlew projects
:projects
------------------------------------------------------------
Root project
------------------------------------------------------------
Root project ‘awsm_app'
+--- Project ':app'
+--- Project ':lintlib'
--- Project ':lintrules'
Project structure
• lintrules
• lintlib
• app
• has dependency to lintlib module
• since lintlib is an Android library module, we
can use the generated lint.jar
$ ./gradlew projects
:projects
------------------------------------------------------------
Root project
------------------------------------------------------------
Root project ‘awsm_app'
+--- Project ':app'
+--- Project ':lintlib'
--- Project ':lintrules'
Project structure
• lintrules
• lintlib
• app
• check your project using with
./gradlew lint
• configure Lint as described here:
http://goo.gl/xABHhy
$ ./gradlew projects
:projects
------------------------------------------------------------
Root project
------------------------------------------------------------
Root project ‘awsm_app'
+--- Project ':app'
+--- Project ':lintlib'
--- Project ':lintrules'
Further information
Testing
• part of lintrules module
• tested with JUnit 4.12 and Easymock 3.3
• register and execute tests as usual in
build.gradle
public class WrongTimberUsageTest extends LintCheckTest {



@Override

protected Detector getDetector() {

return new WrongTimberUsageDetector();

}



@Test

public void testLog() throws Exception {

String expected = ...;

String lintResult = lintFiles(

"WrongTimberTestActivity.java.txt=>" +

"src/test/WrongTimberTestActivity.java");

assertEquals(expectedResult, lintResult);

}

}
Testing
• part of lintrules module
• tested with JUnit 4.12 and Easymock 3.3
• register and execute tests as usual in
build.gradle
• every test should extend LintCheckTest
• custom version of AbstractCheckTest
public class WrongTimberUsageTest extends LintCheckTest {



@Override

protected Detector getDetector() {

return new WrongTimberUsageDetector();

}



@Test

public void testLog() throws Exception {

String expected = ...;

String lintResult = lintFiles(

"WrongTimberTestActivity.java.txt=>" +

"src/test/WrongTimberTestActivity.java");

assertEquals(expectedResult, lintResult);

}

}
Testing
• part of lintrules module
• tested with JUnit 4.12 and Easymock 3.3
• register and execute tests as usual in
build.gradle
• every test should extend LintCheckTest
• need to define the to be used detector
and the selected issues
public class WrongTimberUsageTest extends LintCheckTest {



@Override

protected Detector getDetector() {

return new WrongTimberUsageDetector();

}



@Test

public void testLog() throws Exception {

String expected = ...;

String lintResult = lintFiles(

"WrongTimberTestActivity.java.txt=>" +

"src/test/WrongTimberTestActivity.java");

assertEquals(expectedResult, lintResult);

}

}
Testing
• part of lintrules module
• tested with JUnit 4.12 and Easymock 3.3
• register and execute tests as usual in
build.gradle
• every test should extend LintCheckTest
• need to define the to be used detector
and the selected issues
• perform usual unit tests with the helper
methods including:
• lintFiles
• lintProject
public class WrongTimberUsageTest extends LintCheckTest {



@Override

protected Detector getDetector() {

return new WrongTimberUsageDetector();

}



@Test

public void testLog() throws Exception {

String expected = ...;

String lintResult = lintFiles(

"WrongTimberTestActivity.java.txt=>" +

"src/test/WrongTimberTestActivity.java");

assertEquals(expectedResult, lintResult);

}

}
Testing
• need to specify the files that should be
tested
• put into data/src package in test
resources
• append to all classes suffix .txt
• more examples:
https://goo.gl/Z3gk5U
public class WrongTimberTestActivity extends FragmentActivity {



private static final String TAG = "WrongTimberTestActivity";



@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

Log.d(TAG, "Test android logging");

Timber.d("Test timber logging");

}

}
Debugging
• not possible on real sources without building
lint on your own
• workaround is to debug on your tests
Conclusion
• not well documented API
• sample project:
https://goo.gl/TQt1jV
• to improve code quality
• help new team members
Questions?
Thank you!

Weitere ähnliche Inhalte

Kürzlich hochgeladen

THE SENDAI FRAMEWORK FOR DISASTER RISK REDUCTION
THE SENDAI FRAMEWORK FOR DISASTER RISK REDUCTIONTHE SENDAI FRAMEWORK FOR DISASTER RISK REDUCTION
THE SENDAI FRAMEWORK FOR DISASTER RISK REDUCTIONjhunlian
 
complete construction, environmental and economics information of biomass com...
complete construction, environmental and economics information of biomass com...complete construction, environmental and economics information of biomass com...
complete construction, environmental and economics information of biomass com...asadnawaz62
 
UNIT III ANALOG ELECTRONICS (BASIC ELECTRONICS)
UNIT III ANALOG ELECTRONICS (BASIC ELECTRONICS)UNIT III ANALOG ELECTRONICS (BASIC ELECTRONICS)
UNIT III ANALOG ELECTRONICS (BASIC ELECTRONICS)Dr SOUNDIRARAJ N
 
Crystal Structure analysis and detailed information pptx
Crystal Structure analysis and detailed information pptxCrystal Structure analysis and detailed information pptx
Crystal Structure analysis and detailed information pptxachiever3003
 
Class 1 | NFPA 72 | Overview Fire Alarm System
Class 1 | NFPA 72 | Overview Fire Alarm SystemClass 1 | NFPA 72 | Overview Fire Alarm System
Class 1 | NFPA 72 | Overview Fire Alarm Systemirfanmechengr
 
Research Methodology for Engineering pdf
Research Methodology for Engineering pdfResearch Methodology for Engineering pdf
Research Methodology for Engineering pdfCaalaaAbdulkerim
 
Autonomous emergency braking system (aeb) ppt.ppt
Autonomous emergency braking system (aeb) ppt.pptAutonomous emergency braking system (aeb) ppt.ppt
Autonomous emergency braking system (aeb) ppt.pptbibisarnayak0
 
Comparative study of High-rise Building Using ETABS,SAP200 and SAFE., SAFE an...
Comparative study of High-rise Building Using ETABS,SAP200 and SAFE., SAFE an...Comparative study of High-rise Building Using ETABS,SAP200 and SAFE., SAFE an...
Comparative study of High-rise Building Using ETABS,SAP200 and SAFE., SAFE an...Erbil Polytechnic University
 
Arduino_CSE ece ppt for working and principal of arduino.ppt
Arduino_CSE ece ppt for working and principal of arduino.pptArduino_CSE ece ppt for working and principal of arduino.ppt
Arduino_CSE ece ppt for working and principal of arduino.pptSAURABHKUMAR892774
 
Instrumentation, measurement and control of bio process parameters ( Temperat...
Instrumentation, measurement and control of bio process parameters ( Temperat...Instrumentation, measurement and control of bio process parameters ( Temperat...
Instrumentation, measurement and control of bio process parameters ( Temperat...121011101441
 
Work Experience-Dalton Park.pptxfvvvvvvv
Work Experience-Dalton Park.pptxfvvvvvvvWork Experience-Dalton Park.pptxfvvvvvvv
Work Experience-Dalton Park.pptxfvvvvvvvLewisJB
 
TechTAC® CFD Report Summary: A Comparison of Two Types of Tubing Anchor Catchers
TechTAC® CFD Report Summary: A Comparison of Two Types of Tubing Anchor CatchersTechTAC® CFD Report Summary: A Comparison of Two Types of Tubing Anchor Catchers
TechTAC® CFD Report Summary: A Comparison of Two Types of Tubing Anchor Catcherssdickerson1
 
Internet of things -Arshdeep Bahga .pptx
Internet of things -Arshdeep Bahga .pptxInternet of things -Arshdeep Bahga .pptx
Internet of things -Arshdeep Bahga .pptxVelmuruganTECE
 
Indian Dairy Industry Present Status and.ppt
Indian Dairy Industry Present Status and.pptIndian Dairy Industry Present Status and.ppt
Indian Dairy Industry Present Status and.pptMadan Karki
 
11. Properties of Liquid Fuels in Energy Engineering.pdf
11. Properties of Liquid Fuels in Energy Engineering.pdf11. Properties of Liquid Fuels in Energy Engineering.pdf
11. Properties of Liquid Fuels in Energy Engineering.pdfHafizMudaserAhmad
 
System Simulation and Modelling with types and Event Scheduling
System Simulation and Modelling with types and Event SchedulingSystem Simulation and Modelling with types and Event Scheduling
System Simulation and Modelling with types and Event SchedulingBootNeck1
 
Mine Environment II Lab_MI10448MI__________.pptx
Mine Environment II Lab_MI10448MI__________.pptxMine Environment II Lab_MI10448MI__________.pptx
Mine Environment II Lab_MI10448MI__________.pptxRomil Mishra
 
Energy Awareness training ppt for manufacturing process.pptx
Energy Awareness training ppt for manufacturing process.pptxEnergy Awareness training ppt for manufacturing process.pptx
Energy Awareness training ppt for manufacturing process.pptxsiddharthjain2303
 
Industrial Safety Unit-IV workplace health and safety.ppt
Industrial Safety Unit-IV workplace health and safety.pptIndustrial Safety Unit-IV workplace health and safety.ppt
Industrial Safety Unit-IV workplace health and safety.pptNarmatha D
 

Kürzlich hochgeladen (20)

Designing pile caps according to ACI 318-19.pptx
Designing pile caps according to ACI 318-19.pptxDesigning pile caps according to ACI 318-19.pptx
Designing pile caps according to ACI 318-19.pptx
 
THE SENDAI FRAMEWORK FOR DISASTER RISK REDUCTION
THE SENDAI FRAMEWORK FOR DISASTER RISK REDUCTIONTHE SENDAI FRAMEWORK FOR DISASTER RISK REDUCTION
THE SENDAI FRAMEWORK FOR DISASTER RISK REDUCTION
 
complete construction, environmental and economics information of biomass com...
complete construction, environmental and economics information of biomass com...complete construction, environmental and economics information of biomass com...
complete construction, environmental and economics information of biomass com...
 
UNIT III ANALOG ELECTRONICS (BASIC ELECTRONICS)
UNIT III ANALOG ELECTRONICS (BASIC ELECTRONICS)UNIT III ANALOG ELECTRONICS (BASIC ELECTRONICS)
UNIT III ANALOG ELECTRONICS (BASIC ELECTRONICS)
 
Crystal Structure analysis and detailed information pptx
Crystal Structure analysis and detailed information pptxCrystal Structure analysis and detailed information pptx
Crystal Structure analysis and detailed information pptx
 
Class 1 | NFPA 72 | Overview Fire Alarm System
Class 1 | NFPA 72 | Overview Fire Alarm SystemClass 1 | NFPA 72 | Overview Fire Alarm System
Class 1 | NFPA 72 | Overview Fire Alarm System
 
Research Methodology for Engineering pdf
Research Methodology for Engineering pdfResearch Methodology for Engineering pdf
Research Methodology for Engineering pdf
 
Autonomous emergency braking system (aeb) ppt.ppt
Autonomous emergency braking system (aeb) ppt.pptAutonomous emergency braking system (aeb) ppt.ppt
Autonomous emergency braking system (aeb) ppt.ppt
 
Comparative study of High-rise Building Using ETABS,SAP200 and SAFE., SAFE an...
Comparative study of High-rise Building Using ETABS,SAP200 and SAFE., SAFE an...Comparative study of High-rise Building Using ETABS,SAP200 and SAFE., SAFE an...
Comparative study of High-rise Building Using ETABS,SAP200 and SAFE., SAFE an...
 
Arduino_CSE ece ppt for working and principal of arduino.ppt
Arduino_CSE ece ppt for working and principal of arduino.pptArduino_CSE ece ppt for working and principal of arduino.ppt
Arduino_CSE ece ppt for working and principal of arduino.ppt
 
Instrumentation, measurement and control of bio process parameters ( Temperat...
Instrumentation, measurement and control of bio process parameters ( Temperat...Instrumentation, measurement and control of bio process parameters ( Temperat...
Instrumentation, measurement and control of bio process parameters ( Temperat...
 
Work Experience-Dalton Park.pptxfvvvvvvv
Work Experience-Dalton Park.pptxfvvvvvvvWork Experience-Dalton Park.pptxfvvvvvvv
Work Experience-Dalton Park.pptxfvvvvvvv
 
TechTAC® CFD Report Summary: A Comparison of Two Types of Tubing Anchor Catchers
TechTAC® CFD Report Summary: A Comparison of Two Types of Tubing Anchor CatchersTechTAC® CFD Report Summary: A Comparison of Two Types of Tubing Anchor Catchers
TechTAC® CFD Report Summary: A Comparison of Two Types of Tubing Anchor Catchers
 
Internet of things -Arshdeep Bahga .pptx
Internet of things -Arshdeep Bahga .pptxInternet of things -Arshdeep Bahga .pptx
Internet of things -Arshdeep Bahga .pptx
 
Indian Dairy Industry Present Status and.ppt
Indian Dairy Industry Present Status and.pptIndian Dairy Industry Present Status and.ppt
Indian Dairy Industry Present Status and.ppt
 
11. Properties of Liquid Fuels in Energy Engineering.pdf
11. Properties of Liquid Fuels in Energy Engineering.pdf11. Properties of Liquid Fuels in Energy Engineering.pdf
11. Properties of Liquid Fuels in Energy Engineering.pdf
 
System Simulation and Modelling with types and Event Scheduling
System Simulation and Modelling with types and Event SchedulingSystem Simulation and Modelling with types and Event Scheduling
System Simulation and Modelling with types and Event Scheduling
 
Mine Environment II Lab_MI10448MI__________.pptx
Mine Environment II Lab_MI10448MI__________.pptxMine Environment II Lab_MI10448MI__________.pptx
Mine Environment II Lab_MI10448MI__________.pptx
 
Energy Awareness training ppt for manufacturing process.pptx
Energy Awareness training ppt for manufacturing process.pptxEnergy Awareness training ppt for manufacturing process.pptx
Energy Awareness training ppt for manufacturing process.pptx
 
Industrial Safety Unit-IV workplace health and safety.ppt
Industrial Safety Unit-IV workplace health and safety.pptIndustrial Safety Unit-IV workplace health and safety.ppt
Industrial Safety Unit-IV workplace health and safety.ppt
 

Empfohlen

AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfmarketingartwork
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024Neil Kimberley
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)contently
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024Albert Qian
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsKurio // The Social Media Age(ncy)
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Search Engine Journal
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summarySpeakerHub
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next Tessa Mero
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentLily Ray
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best PracticesVit Horky
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project managementMindGenius
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...RachelPearson36
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Applitools
 
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at WorkGetSmarter
 

Empfohlen (20)

AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
 
Skeleton Culture Code
Skeleton Culture CodeSkeleton Culture Code
Skeleton Culture Code
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
 
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work
 
ChatGPT webinar slides
ChatGPT webinar slidesChatGPT webinar slides
ChatGPT webinar slides
 
More than Just Lines on a Map: Best Practices for U.S Bike Routes
More than Just Lines on a Map: Best Practices for U.S Bike RoutesMore than Just Lines on a Map: Best Practices for U.S Bike Routes
More than Just Lines on a Map: Best Practices for U.S Bike Routes
 

The Power of Custom Lint Checks - droidcon Berlin 2015

  • 1. The Power of Custom Lint Checks droidcon Berlin | 05.06.2015 | Marc Prengemann
  • 2. About me Marc Prengemann Working student Mail: marc@wire.com Wire: marcprengemann@gmail.com Github: winterDroid Google+: Marc Prengemann
  • 5. When should I use Lint? • To ensure code quality • Focus in reviews on real code • Prevent people from misusing internal libraries … but what are the challenges? • @Beta • Getting familiar with the Lint API • Integrating within your Gradle Build • Debugging / Testing
  • 6. Getting started with own Checks
  • 7. Test ideas • Fragments and Activities should extend your BaseClass • Use ViewUtils instead of finding and casting a View • Don’t check floats for equality - use Float.equals instead • Find leaking resources • Enforce Naming conventions • Find hardcoded values in XMLs
  • 8. A real example • Timber • logger by Jake Wharton • https://github.com/JakeWharton/ timber • want to create a detector that finds misuse of android.util.Log instead of Timber
  • 9. Detector • responsible for scanning through code and to find issues and report them public class WrongTimberUsageDetector extends Detector implements Detector.JavaScanner {
 
 public static final Issue ISSUE = ...;
 
 @Override
 public List<String> getApplicableMethodNames() {
 return Arrays.asList("v", "d", "i", "w", "e", "wtf");
 }
 
 @Override
 public void visitMethod(@NonNull JavaContext context, 
 AstVisitor visitor, 
 @NonNull MethodInvocation node) {
 if (!(node.astOperand() instanceof VariableReference)) {
 return;
 }
 VariableReference ref = (VariableReference) node.astOperand();
 if ("Log".equals(ref.astIdentifier().astValue())) {
 context.report(ISSUE,
 node,
 context.getLocation(node),
 "Using 'Log' instead of 'Timber'");
 }
 }
 }
  • 10. Detector • responsible for scanning through code and to find issues and report them • most detectors implement one or more scanner interfaces that depend on the specified scope • Detector.XmlScanner • Detector.JavaScanner • Detector.ClassScanner public class WrongTimberUsageDetector extends Detector implements Detector.JavaScanner {
 
 public static final Issue ISSUE = ...;
 
 @Override
 public List<String> getApplicableMethodNames() {
 return Arrays.asList("v", "d", "i", "w", "e", "wtf");
 }
 
 @Override
 public void visitMethod(@NonNull JavaContext context, 
 AstVisitor visitor, 
 @NonNull MethodInvocation node) {
 if (!(node.astOperand() instanceof VariableReference)) {
 return;
 }
 VariableReference ref = (VariableReference) node.astOperand();
 if ("Log".equals(ref.astIdentifier().astValue())) {
 context.report(ISSUE,
 node,
 context.getLocation(node),
 "Using 'Log' instead of 'Timber'");
 }
 }
 }
  • 11. Detector • responsible for scanning through code and finding issue instances and reporting them • most detectors implement one or more scanner interfaces • can detect multiple different issues • allows you to have different severities for different types of issues public class WrongTimberUsageDetector extends Detector implements Detector.JavaScanner {
 
 public static final Issue ISSUE = ...;
 
 @Override
 public List<String> getApplicableMethodNames() {
 return Arrays.asList("v", "d", "i", "w", "e", "wtf");
 }
 
 @Override
 public void visitMethod(@NonNull JavaContext context, 
 AstVisitor visitor, 
 @NonNull MethodInvocation node) {
 if (!(node.astOperand() instanceof VariableReference)) {
 return;
 }
 VariableReference ref = (VariableReference) node.astOperand();
 if ("Log".equals(ref.astIdentifier().astValue())) {
 context.report(ISSUE,
 node,
 context.getLocation(node),
 "Using 'Log' instead of 'Timber'");
 }
 }
 }
  • 12. Detector • responsible for scanning through code and finding issue instances and reporting them • most detectors implement one or more scanner interfaces • can detect multiple different issues • define the calls that should be analyzed • overwritten method depends on implemented scanner interface • depends on the goal of the detector public class WrongTimberUsageDetector extends Detector implements Detector.JavaScanner {
 
 public static final Issue ISSUE = ...;
 
 @Override
 public List<String> getApplicableMethodNames() {
 return Arrays.asList("v", "d", "i", "w", "e", "wtf");
 }
 
 @Override
 public void visitMethod(@NonNull JavaContext context, 
 AstVisitor visitor, 
 @NonNull MethodInvocation node) {
 if (!(node.astOperand() instanceof VariableReference)) {
 return;
 }
 VariableReference ref = (VariableReference) node.astOperand();
 if ("Log".equals(ref.astIdentifier().astValue())) {
 context.report(ISSUE,
 node,
 context.getLocation(node),
 "Using 'Log' instead of 'Timber'");
 }
 }
 }
  • 13. Detector • responsible for scanning through code and finding issue instances and reporting them • most detectors implement one or more scanner interfaces • can detect multiple different issues • define the calls that should be analyzed • analyze the found calls • overwritten method depends on implemented scanner interface • depends on the goal of the detector public class WrongTimberUsageDetector extends Detector implements Detector.JavaScanner {
 
 public static final Issue ISSUE = ...;
 
 @Override
 public List<String> getApplicableMethodNames() {
 return Arrays.asList("v", "d", "i", "w", "e", "wtf");
 }
 
 @Override
 public void visitMethod(@NonNull JavaContext context, 
 AstVisitor visitor, 
 @NonNull MethodInvocation node) {
 if (!(node.astOperand() instanceof VariableReference)) {
 return;
 }
 VariableReference ref = (VariableReference) node.astOperand();
 if ("Log".equals(ref.astIdentifier().astValue())) {
 context.report(ISSUE,
 node,
 context.getLocation(node),
 "Using 'Log' instead of 'Timber'");
 }
 }
 }
  • 14. Detector • responsible for scanning through code and finding issue instances and reporting them • most detectors implement one or more scanner interfaces • can detect multiple different issues • define the calls that should be analyzed • analyze the found calls • report the found issue • specify the location • report() will handle to suppress warnings • add a message for the warning public class WrongTimberUsageDetector extends Detector implements Detector.JavaScanner {
 
 public static final Issue ISSUE = ...;
 
 @Override
 public List<String> getApplicableMethodNames() {
 return Arrays.asList("v", "d", "i", "w", "e", "wtf");
 }
 
 @Override
 public void visitMethod(@NonNull JavaContext context, 
 AstVisitor visitor, 
 @NonNull MethodInvocation node) {
 if (!(node.astOperand() instanceof VariableReference)) {
 return;
 }
 VariableReference ref = (VariableReference) node.astOperand();
 if ("Log".equals(ref.astIdentifier().astValue())) {
 context.report(ISSUE,
 node,
 context.getLocation(node),
 "Using 'Log' instead of 'Timber'");
 }
 }
 }
  • 15. Issue • potential bug in an Android application • is discovered by a Detector • are exposed to the user public static final Issue ISSUE = Issue.create("LogNotTimber",
 "Used Log instead of Timber",
 "Since Timber is included in the project, " + "it is likely that calls to Log should "
 + "instead be going to Timber.",
 Category.MESSAGES,
 5,
 Severity.WARNING,
 new Implementation(WrongTimberUsageDetector.class, Scope.JAVA_FILE_SCOPE));
  • 16. Issue • the id of the issue • should be unique • recommended to add the package name as a prefix like com.wire.LogNotTimber public static final Issue ISSUE = Issue.create(“LogNotTimber", "Used Log instead of Timber",
 "Since Timber is included in the project, “ + "it is likely that calls to Log should "
 + "instead be going to Timber.",
 Category.MESSAGES,
 5,
 Severity.WARNING,
 new Implementation(WrongTimberUsageDetector.class, Scope.JAVA_FILE_SCOPE));
  • 17. Issue • the id of the issue • short summary • typically 5-6 words or less • describe the problem rather than the fix public static final Issue ISSUE = Issue.create(“LogNotTimber", "Used Log instead of Timber",
 "Since Timber is included in the project, “ + "it is likely that calls to Log should "
 + "instead be going to Timber.",
 Category.MESSAGES,
 5,
 Severity.WARNING,
 new Implementation(WrongTimberUsageDetector.class, Scope.JAVA_FILE_SCOPE));
  • 18. Issue • the id of the issue • short summary • full explanation of the issue • should include a suggestion how to fix it public static final Issue ISSUE = Issue.create(“LogNotTimber", "Used Log instead of Timber",
 "Since Timber is included in the project, “ + "it is likely that calls to Log should "
 + "instead be going to Timber.",
 Category.MESSAGES,
 5,
 Severity.WARNING,
 new Implementation(WrongTimberUsageDetector.class, Scope.JAVA_FILE_SCOPE));
  • 19. Issue • the id of the issue • short summary • full explanation of the issue • the associated category, if any • Lint • Correctness (incl. Messages) • Security • Performance • Usability (incl. Icons, Typography) • Accessibility • Internationalization • Bi-directional text public static final Issue ISSUE = Issue.create(“LogNotTimber", "Used Log instead of Timber",
 "Since Timber is included in the project, “ + "it is likely that calls to Log should "
 + "instead be going to Timber.",
 Category.MESSAGES,
 5,
 Severity.WARNING,
 new Implementation(WrongTimberUsageDetector.class, Scope.JAVA_FILE_SCOPE));
  • 20. Issue • the id of the issue • short summary • full explanation of the issue • the associated category, if any • the priority • a number from 1 to 10 • 10 being most important/severe public static final Issue ISSUE = Issue.create(“LogNotTimber", "Used Log instead of Timber",
 "Since Timber is included in the project, “ + "it is likely that calls to Log should "
 + "instead be going to Timber.",
 Category.MESSAGES,
 5,
 Severity.WARNING,
 new Implementation(WrongTimberUsageDetector.class, Scope.JAVA_FILE_SCOPE));
  • 21. Issue • the id of the issue • short summary • full explanation of the issue • the associated category, if any • the priority • the default severity of the issue • Fatal • Error • Warning • Informational • Ignore public static final Issue ISSUE = Issue.create(“LogNotTimber", "Used Log instead of Timber",
 "Since Timber is included in the project, “ + "it is likely that calls to Log should "
 + "instead be going to Timber.",
 Category.MESSAGES,
 5,
 Severity.WARNING,
 new Implementation(WrongTimberUsageDetector.class, Scope.JAVA_FILE_SCOPE));
  • 22. Issue • the id of the issue • short summary • full explanation of the issue • the associated category, if any • the priority • the default severity of the issue • the default implementation for this issue • maps to the Detector class • specifies the scope of the implementation public static final Issue ISSUE = Issue.create(“LogNotTimber", "Used Log instead of Timber",
 "Since Timber is included in the project, “ + "it is likely that calls to Log should "
 + "instead be going to Timber.",
 Category.MESSAGES,
 5,
 Severity.WARNING,
 new Implementation(WrongTimberUsageDetector.class, Scope.JAVA_FILE_SCOPE));
  • 23. Issue • the id of the issue • short summary • full explanation of the issue • the associated category, if any • the priority • the default severity of the issue • the default implementation for this issue • the scope of the implementation • describes set of files a detector must consider when performing its analysis • Include: • Resource files / folder • Java files • Class files public static final Issue ISSUE = Issue.create(“LogNotTimber", "Used Log instead of Timber",
 "Since Timber is included in the project, “ + "it is likely that calls to Log should "
 + "instead be going to Timber.",
 Category.MESSAGES,
 5,
 Severity.WARNING,
 new Implementation(WrongTimberUsageDetector.class, Scope.JAVA_FILE_SCOPE));
  • 24. Issue Registry • provide list of checks to be performed • return a list of Issues in getIssues() public class CustomIssueRegistry extends IssueRegistry {
 @Override
 public List<Issue> getIssues() {
 return Arrays.asList(WrongTimberUsageDetector.ISSUE);
 }
 }
  • 26. Project structure • lintrules • Java module • source of all custom detectors and our IssueRegistry • specify reference in build.gradle as attribute Lint-Registry • will export a lint.jar $ ./gradlew projects :projects ------------------------------------------------------------ Root project ------------------------------------------------------------ Root project ‘awsm_app' +--- Project ':app' +--- Project ':lintlib' --- Project ':lintrules' jar { manifest { attributes 'Manifest-Version': 1.0 attributes 'Lint-Registry': 'com.checks.CustomIssueRegistry' } }
  • 27. Project structure • lintrules • lintlib • Android library module • has a dependency to lintrules $ ./gradlew projects :projects ------------------------------------------------------------ Root project ------------------------------------------------------------ Root project ‘awsm_app' +--- Project ':app' +--- Project ':lintlib' --- Project ':lintrules'
  • 28. Project structure • lintrules • lintlib • app • has dependency to lintlib module • since lintlib is an Android library module, we can use the generated lint.jar $ ./gradlew projects :projects ------------------------------------------------------------ Root project ------------------------------------------------------------ Root project ‘awsm_app' +--- Project ':app' +--- Project ':lintlib' --- Project ':lintrules'
  • 29. Project structure • lintrules • lintlib • app • check your project using with ./gradlew lint • configure Lint as described here: http://goo.gl/xABHhy $ ./gradlew projects :projects ------------------------------------------------------------ Root project ------------------------------------------------------------ Root project ‘awsm_app' +--- Project ':app' +--- Project ':lintlib' --- Project ':lintrules'
  • 31. Testing • part of lintrules module • tested with JUnit 4.12 and Easymock 3.3 • register and execute tests as usual in build.gradle public class WrongTimberUsageTest extends LintCheckTest {
 
 @Override
 protected Detector getDetector() {
 return new WrongTimberUsageDetector();
 }
 
 @Test
 public void testLog() throws Exception {
 String expected = ...;
 String lintResult = lintFiles(
 "WrongTimberTestActivity.java.txt=>" +
 "src/test/WrongTimberTestActivity.java");
 assertEquals(expectedResult, lintResult);
 }
 }
  • 32. Testing • part of lintrules module • tested with JUnit 4.12 and Easymock 3.3 • register and execute tests as usual in build.gradle • every test should extend LintCheckTest • custom version of AbstractCheckTest public class WrongTimberUsageTest extends LintCheckTest {
 
 @Override
 protected Detector getDetector() {
 return new WrongTimberUsageDetector();
 }
 
 @Test
 public void testLog() throws Exception {
 String expected = ...;
 String lintResult = lintFiles(
 "WrongTimberTestActivity.java.txt=>" +
 "src/test/WrongTimberTestActivity.java");
 assertEquals(expectedResult, lintResult);
 }
 }
  • 33. Testing • part of lintrules module • tested with JUnit 4.12 and Easymock 3.3 • register and execute tests as usual in build.gradle • every test should extend LintCheckTest • need to define the to be used detector and the selected issues public class WrongTimberUsageTest extends LintCheckTest {
 
 @Override
 protected Detector getDetector() {
 return new WrongTimberUsageDetector();
 }
 
 @Test
 public void testLog() throws Exception {
 String expected = ...;
 String lintResult = lintFiles(
 "WrongTimberTestActivity.java.txt=>" +
 "src/test/WrongTimberTestActivity.java");
 assertEquals(expectedResult, lintResult);
 }
 }
  • 34. Testing • part of lintrules module • tested with JUnit 4.12 and Easymock 3.3 • register and execute tests as usual in build.gradle • every test should extend LintCheckTest • need to define the to be used detector and the selected issues • perform usual unit tests with the helper methods including: • lintFiles • lintProject public class WrongTimberUsageTest extends LintCheckTest {
 
 @Override
 protected Detector getDetector() {
 return new WrongTimberUsageDetector();
 }
 
 @Test
 public void testLog() throws Exception {
 String expected = ...;
 String lintResult = lintFiles(
 "WrongTimberTestActivity.java.txt=>" +
 "src/test/WrongTimberTestActivity.java");
 assertEquals(expectedResult, lintResult);
 }
 }
  • 35. Testing • need to specify the files that should be tested • put into data/src package in test resources • append to all classes suffix .txt • more examples: https://goo.gl/Z3gk5U public class WrongTimberTestActivity extends FragmentActivity {
 
 private static final String TAG = "WrongTimberTestActivity";
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.main);
 Log.d(TAG, "Test android logging");
 Timber.d("Test timber logging");
 }
 }
  • 36. Debugging • not possible on real sources without building lint on your own • workaround is to debug on your tests
  • 37. Conclusion • not well documented API • sample project: https://goo.gl/TQt1jV • to improve code quality • help new team members