/* * SonarQube Java * Copyright (C) 2012-2016 SonarSource SA * mailto:contact AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.sonar.java.cfg; import org.junit.Test; import org.sonar.java.cfg.CFG.Block; import org.sonar.plugins.java.api.tree.Tree; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; public class CFGLoopTest { private static final CFGTestLoader loader = new CFGTestLoader("src/test/files/cfg/CFGLoopTest.java"); private static List<CFG.Block> sorted(Collection<CFG.Block> collection) { List<CFG.Block> answer = new ArrayList<>(collection); Collections.sort(answer, (o1, o2) -> { // Use order of IDs return Integer.compare(o1.id(), o2.id()); }); return answer; } @Test public void simpleWhileLoop() { String methodName = "simpleWhileLoop"; final CFG cfg = buildCFG(methodName); Map<Tree, CFGLoop> loops = CFGLoop.getCFGLoops(cfg); assertThat(loops).hasSize(1); CFGLoop loop = loops.values().iterator().next(); List<Block> blocks = sorted(cfg.blocks()); assertThat(loop.startingBlock()).isSameAs(blocks.get(2)); assertThat(loop.blocks()).containsOnly(blocks.get(1)); assertThat(loop.hasNoWayOut()).isTrue(); } private CFG buildCFG(String methodName) { return CFG.build(loader.getMethod("CFGLoopTest", methodName)); } @Test public void simpleWhileLoopWithBreak() { final CFG cfg = buildCFG("simpleWhileLoopWithBreak"); Map<Tree, CFGLoop> loops = CFGLoop.getCFGLoops(cfg); assertThat(loops).hasSize(1); CFGLoop loop = loops.values().iterator().next(); List<Block> blocks = sorted(cfg.blocks()); assertThat(loop.startingBlock()).isSameAs(blocks.get(3)); assertThat(loop.blocks()).containsOnly(blocks.get(2), blocks.get(1)); assertThat(loop.hasNoWayOut()).isFalse(); } @Test public void simpleForLoop() { final CFG cfg = buildCFG("simpleForLoop"); Map<Tree, CFGLoop> loops = CFGLoop.getCFGLoops(cfg); assertThat(loops).hasSize(1); CFGLoop loop = loops.values().iterator().next(); List<Block> blocks = sorted(cfg.blocks()); assertThat(loop.startingBlock()).isSameAs(blocks.get(3)); assertThat(loop.blocks()).containsOnly(blocks.get(1), blocks.get(2)); assertThat(loop.hasNoWayOut()).isTrue(); } @Test public void embeddedMixedLoops() { final CFG cfg = buildCFG("embeddedMixedLoops"); Map<Tree, CFGLoop> loops = CFGLoop.getCFGLoops(cfg); assertThat(loops).hasSize(2); List<Block> blocks = sorted(cfg.blocks()); CFGLoop loop = loops.get(blocks.get(11).terminator()); assertThat(loop.startingBlock()).isSameAs(blocks.get(11)); assertThat(loop.blocks()).containsOnly(blocks.get(10), blocks.get(9), blocks.get(4), blocks.get(3), blocks.get(2), blocks.get(1)); assertThat(loop.hasNoWayOut()).isTrue(); loop = loops.get(blocks.get(9).terminator()); assertThat(loop.startingBlock()).isSameAs(blocks.get(9)); assertThat(loop.blocks()).containsOnly(blocks.get(8), blocks.get(7), blocks.get(6), blocks.get(5)); assertThat(loop.hasNoWayOut()).isTrue(); } @Test public void mixedWithForEach() { final CFG cfg = buildCFG("mixedWithForEach"); Map<Tree, CFGLoop> loops = CFGLoop.getCFGLoops(cfg); // ForEach loops are not identified as loops! assertThat(loops).hasSize(1); List<Block> blocks = sorted(cfg.blocks()); CFGLoop loop = loops.values().iterator().next(); assertThat(loop.startingBlock()).isSameAs(blocks.get(8)); assertThat(loop.blocks()).containsOnly(blocks.get(7), blocks.get(6)); } @Test public void doWhile() { final CFG cfg = buildCFG("doWhile"); Map<Tree, CFGLoop> loops = CFGLoop.getCFGLoops(cfg); assertThat(loops).hasSize(1); List<Block> blocks = sorted(cfg.blocks()); CFGLoop loop = loops.values().iterator().next(); assertThat(loop.startingBlock()).isSameAs(blocks.get(1)); assertThat(loop.blocks()).containsOnly(blocks.get(2)); } @Test public void minimalForLoop() { final CFG cfg = buildCFG("minimalForLoop"); Map<Tree, CFGLoop> loops = CFGLoop.getCFGLoops(cfg); assertThat(loops).hasSize(1); List<Block> blocks = sorted(cfg.blocks()); CFGLoop loop = loops.values().iterator().next(); assertThat(loop.startingBlock()).isSameAs(blocks.get(2)); assertThat(loop.blocks()).containsOnly(blocks.get(1)); assertThat(loop.hasNoWayOut()).isFalse(); } @Test public void emptyFor() { final CFG cfg = buildCFG("emptyFor"); Map<Tree, CFGLoop> loops = CFGLoop.getCFGLoops(cfg); assertThat(loops).hasSize(1); CFGLoop loop = loops.values().iterator().next(); assertThat(loop.blocks()).isEmpty(); assertThat(loop.hasNoWayOut()).isTrue(); } @Test public void forWithOnlyInitializer() { final CFG cfg = buildCFG("forWithOnlyInitializer"); Map<Tree, CFGLoop> loops = CFGLoop.getCFGLoops(cfg); assertThat(loops).hasSize(1); CFGLoop loop = loops.values().iterator().next(); assertThat(loop.blocks()).isEmpty(); assertThat(loop.hasNoWayOut()).isTrue(); } @Test public void emptyConditionFor() { final CFG cfg = buildCFG("emptyConditionFor"); Map<Tree, CFGLoop> loops = CFGLoop.getCFGLoops(cfg); assertThat(loops).hasSize(1); List<Block> blocks = sorted(cfg.blocks()); CFGLoop loop = loops.values().iterator().next(); assertThat(loop.blocks()).containsOnly(blocks.get(1)); assertThat(loop.hasNoWayOut()).isTrue(); } @Test public void almostEmptyConditionFor() { final CFG cfg = buildCFG("almostEmptyConditionFor"); Map<Tree, CFGLoop> loops = CFGLoop.getCFGLoops(cfg); assertThat(loops).hasSize(1); List<Block> blocks = sorted(cfg.blocks()); CFGLoop loop = loops.values().iterator().next(); assertThat(loop.blocks()).containsOnly(blocks.get(1)); assertThat(loop.hasNoWayOut()).isTrue(); } @Test public void embeddedLoops() { final CFG cfg = buildCFG("embeddedLoops"); Map<Tree, CFGLoop> loops = CFGLoop.getCFGLoops(cfg); assertThat(loops).hasSize(2); List<Block> blocks = sorted(cfg.blocks()); CFGLoop loop = loops.get(blocks.get(6).terminator()); assertThat(loop.blocks()).containsOnly(blocks.get(5), blocks.get(3)); assertThat(loop.successors()).containsOnly(blocks.get(1), blocks.get(4)); loop = loops.get(blocks.get(3).terminator()); assertThat(loop.blocks()).containsOnly(blocks.get(2)); assertThat(loop.successors()).containsOnly(blocks.get(1)); } @Test public void embeddedLoopsReturnInInnermost() { final CFG cfg = buildCFG("embeddedLoopsReturnInInnermost"); Map<Tree, CFGLoop> loops = CFGLoop.getCFGLoops(cfg); assertThat(loops).hasSize(2); List<Block> blocks = sorted(cfg.blocks()); CFGLoop loop = loops.get(blocks.get(5).terminator()); assertThat(loop.blocks()).containsOnly(blocks.get(4), blocks.get(3)); assertThat(loop.successors()).containsOnly(blocks.get(1)); loop = loops.get(blocks.get(3).terminator()); assertThat(loop.blocks()).containsOnly(blocks.get(2)); assertThat(loop.successors()).containsOnly(blocks.get(1)); } @Test public void doubleReturnWhileLoop() { final CFG cfg = buildCFG("doubleReturnWhileLoop"); Map<Tree, CFGLoop> loops = CFGLoop.getCFGLoops(cfg); assertThat(loops).hasSize(2); List<Block> blocks = sorted(cfg.blocks()); CFGLoop loop = loops.get(blocks.get(6).terminator()); assertThat(loop.blocks()).containsOnly(blocks.get(5), blocks.get(3)); assertThat(loop.successors()).containsOnly(blocks.get(1), blocks.get(4)); loop = loops.get(blocks.get(3).terminator()); assertThat(loop.blocks()).containsOnly(blocks.get(2)); assertThat(loop.successors()).containsOnly(blocks.get(1)); } }