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

feat(EC-514): Handle Motion Sensor Leakage when started but not stopped #30

Merged
merged 1 commit into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* ecoCode iOS plugin - Help the earth, adopt this green plugin for your applications
* Copyright © 2023 green-code-initiative (https://www.ecocode.io/)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.ecocode.ios.swift.checks.sobriety;

import io.ecocode.ios.swift.SwiftRuleCheck;
import io.ecocode.ios.swift.antlr.generated.Swift5Parser;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNodeImpl;
import org.sonar.check.Rule;

@Rule(key = "EC514")
public class MotionSensorLeakCheck extends SwiftRuleCheck {
private static final String DEFAULT_ISSUE_MESSAGE = "Any motion sensor started should be stopped.";
private boolean motionSensorStarted = false;
private boolean motionSensorStopped = false;
Swift5Parser.ExpressionContext id;

@Override
public void apply(ParseTree tree) {
if (tree instanceof Swift5Parser.ExpressionContext && tree.getText().contains("startAccelerometerUpdates")) {
id = (Swift5Parser.ExpressionContext) tree;
motionSensorStarted = true;
}

if (tree instanceof Swift5Parser.ExpressionContext
&& (tree.getText().contains("stopAccelerometerUpdates"))) {
motionSensorStopped = true;
}

if (tree instanceof TerminalNodeImpl && tree.getText().equals("<EOF>")) {
if (motionSensorStarted && !motionSensorStopped) {
this.recordIssue(id.getStart().getStartIndex(), DEFAULT_ISSUE_MESSAGE);
}
motionSensorStarted = false;
motionSensorStopped = false;
}
}
}
3 changes: 3 additions & 0 deletions swift-lang/src/main/resources/ecocode_swift_profile.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@
"EC509",
"EC512",
"EC513",
"EC514",
"EC519",
"EC520",
"EC522",
"EC524",
"EC530",
"EC533",
"EC534",
"EC603"
]
}
45 changes: 45 additions & 0 deletions swift-lang/src/main/resources/io/ecocode/rules/swift/EC514.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<img src="http://www.neomades.com/extern/partage/ecoCode/2sur5_1x.png">
<p>Most iOS devices have built-in sensors that measure motion, orientation, and various environmental conditions.
Additionally, they have image sensors (a.k.a. Camera) and geo-positioning sensors (a.k.a. GPS).

The common point of all these sensors is that they consume significant power while in use. Their common issue is
processing data unnecessarily when the app is in an idle state, typically when it enters the background or becomes
inactive.

<code>Consequently, calls to start and stop sensor updates must be carefully managed for motion sensor:
CMMotionManager#startAccelerometerUpdates()/CMMotionManager#stopAccelerometerUpdates().
Failing to do so can drain the battery quickly.</code></p>
<h2>Noncompliant Code Example</h2>
<pre>
import CoreMotion

let motionManager = CMMotionManager()

func startMotionUpdates() {
if motionManager.isAccelerometerAvailable {
motionManager.startAccelerometerUpdates(to: .main) { data, error in
// Handle accelerometer updates
}
}
}
</pre>
<h2>Compliant Code Example</h2>
<pre>
import CoreMotion

let motionManager = CMMotionManager()

func startMotionUpdates() {
if motionManager.isAccelerometerAvailable {
motionManager.startAccelerometerUpdates(to: .main) { data, error in
// Handle accelerometer updates
}
}
}

func stopMotionUpdates() {
if motionManager.isAccelerometerActive {
motionManager.stopAccelerometerUpdates()
}
}
</pre>
18 changes: 18 additions & 0 deletions swift-lang/src/main/resources/io/ecocode/rules/swift/EC514.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"key": "EC514",
"title": "Motion Sensor Leakage",
"defaultSeverity": "Major",
"description": "Any motion sensor started should be stopped.",
"status": "ready",
"remediation": {
"func": "Constant/Issue",
"constantCost": "5min"
},
"tags": [
"ecocode",
"environment",
"sobriety",
"eco-design"
],
"type": "CODE_SMELL"
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ public void testMetadata() {
}

@Test
public void testRegistredRules() {
assertThat(repository.rules()).hasSize(12);
public void testRegisteredRules() {
assertThat(repository.rules()).hasSize(13);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* ecoCode iOS plugin - Help the earth, adopt this green plugin for your applications
* Copyright © 2023 green-code-initiative (https://www.ecocode.io/)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.ecocode.ios.swift.checks.sobriety;

import io.ecocode.ios.swift.checks.CheckTestHelper;
import org.junit.Test;
import org.sonar.api.batch.sensor.internal.SensorContextTester;
import org.sonar.api.batch.sensor.issue.Issue;
import org.sonar.api.batch.sensor.issue.IssueLocation;

import java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;

public class MotionSensorLeakCheckTest {
@Test
public void SensorLeak_trigger() {
SensorContextTester context = CheckTestHelper.analyzeTestFile("checks/sobriety/MotionSensorLeak_trigger.swift");
assertThat(context.allIssues()).hasSize(1);
Optional<Issue> issue = context.allIssues().stream().findFirst();
issue.ifPresent(i -> {
assertThat(i.ruleKey().rule()).isEqualTo("EC514");

assertThat(i.ruleKey().repository()).isEqualTo("ecoCode-swift");
IssueLocation location = i.primaryLocation();
assertThat(location.textRange().start().line()).isEqualTo(7);

});
}

@Test
public void SensorLeak_no_trigger() {
SensorContextTester context = CheckTestHelper.analyzeTestFile("checks/sobriety/MotionSensorLeak_no_trigger.swift");
assertThat(context.allIssues()).isEmpty();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import CoreMotion

let motionManager = CMMotionManager()

func startMotionUpdates() {
if motionManager.isAccelerometerAvailable {
motionManager.startAccelerometerUpdates(to: .main) { data, error in
// Handle accelerometer updates
}
}
motionManager.magnetometerUpdateInterval = 0.1
}

func stopMotionUpdates() {
if motionManager.isAccelerometerActive {
motionManager.stopAccelerometerUpdates()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import CoreMotion

let motionManager = CMMotionManager()

func startMotionUpdates() {
if motionManager.isAccelerometerAvailable {
motionManager.startAccelerometerUpdates(to: .main) { data, error in
// Handle accelerometer updates
}
}
motionManager.magnetometerUpdateInterval = 0.1
}
Loading