åäœãã¹ãã¯ãã³ãŒããæå³ãããšããã«åäœããããšã確èªããã®ã«åœ¹ç«ã¡ãŸãã ãã¹ãã¡ããªãã¯ã®1ã€ã¯ãã³ãŒãè¡ã®ã«ãã¬ããžã®å²å ïŒã©ã€ã³ã³ãŒãã«ãã¬ããžïŒã§ãã
ãããããã®ã€ã³ãžã±ãŒã¿ãŒã¯ã©ã®çšåºŠæ£ããã®ã§ããããïŒ ããã¯å®çšçãªæå³ãããã圌ãä¿¡é Œã§ããŸããïŒ çµå±ã®ãšããããã¹ããããã¹ãŠã®assert
è¡ãåé€ããããåã«assertSame(1, 1)
ã§çœ®ãæãããšããã¹ãã¯äœããã¹ãããŸãããã100ïŒ
ã®ã³ãŒãã«ãã¬ããžãåŸãããŸãã
ãã¹ãã«ã©ãã»ã©èªä¿¡ããããŸããïŒ é¢æ°ã®ãã¹ãŠã®ãã©ã³ããã«ããŒããŠããŸããïŒ åœŒãã¯äœã§ããã¹ãããŸããïŒ
ãã®è³ªåã«å¯Ÿããçãã¯ãçªç¶å€ç°ãã¹ãã«ãã£ãŠäžããããŸãã
çªç¶å€ç°ãã¹ãã¯ããœãŒã¹ã³ãŒããžã®ããããçš®é¡ã®å€æŽã«åºã¥ãããœãããŠã§ã¢ãã¹ãæ¹æ³ã§ãããäžé£ã®èªåãã¹ãã§ãããã®å€æŽã«å¯Ÿããåå¿ããã¹ãããŸãã ã³ãŒãã®å€æŽåŸã«ãã¹ããæ£åžžã«å®è¡ãããå Žåãã³ãŒãã¯ãã¹ãã®å¯Ÿè±¡å€ã§ããããèšè¿°ããããã¹ããç¡å¹ã§ãã èªåãã¹ãã®ã»ããã®æå¹æ§ã決å®ããåºæºã¯ãçªç¶å€ç°ã¹ã³ã¢ã€ã³ãžã±ãŒã¿ïŒMSIïŒãšåŒã°ããŸãã
çªç¶å€ç°ãã¹ãã®çè«ããããã€ãã®æŠå¿µã玹ä»ããŸãã
ãã®æè¡ã䜿çšããã«ã¯ãæããã«ãœãŒã¹ã³ãŒããç¹å®ã®ãã¹ãã»ãããå¿
èŠã§ãïŒç°¡åã«ããããã«ã ãŠããããã¹ãã«ã€ããŠèª¬æããŸã ïŒã
ãã®åŸããœãŒã¹ã³ãŒãã®åã
ã®éšåã倿Žãããã¹ããããã«ã©ã®ããã«å¿çããããèŠãããšãã§ããŸãã
ãœãŒã¹ã³ãŒããžã®1ã€ã®å€æŽã¯MutationãšåŒã°ããŸãã ããšãã°ã2é
æŒç®å"+"
ã2é
"-"
ã«å€æŽããããšã¯ã³ãŒã倿Žã§ãã
ãã¥ãŒããŒã·ã§ã³ã®çµæã¯ãã¥ãŒã¿ã³ãã§ããã€ãŸããããã¯æ°ãããã¥ãŒããŒã·ã§ã³ããããœãŒã¹ã³ãŒãã§ãã
ã³ãŒãå
ã®ä»»æã®æŒç®åã®å€ç°ïŒããã³äœçŸãã®ããããååšããïŒã¯ããã¹ããå®è¡ããå¿
èŠãããæ°ããå€ç°äœã«ã€ãªãããŸãã
"+"
ãã"-"
ãžã®å€æŽã«å ããŠãä»ã®å€ãã®çªç¶å€ç°æŒç®åïŒ Mutation Operator ã Mutator ïŒ-æ¡ä»¶ã®æåŠã颿°ã®æ»ãå€ã®å€æŽãã³ãŒãè¡ã®åé€ãªã©ããããŸãã
ãã®ãããå€ç°ãã¹ãã§ã¯ã³ãŒãããå€ãã®å€ç°äœãäœæãããããããããã¹ããå®è¡ããŠæåãããã©ããããã§ãã¯ããŸãã ãã¹ãã倱æããå Žåããã¹ãŠãæ£åžžã§ãããã³ãŒãã®å€æŽã«å¿çãããšã©ãŒããã£ããããŸããã ãã®ãããªçªç¶å€ç°äœã¯æ®ºããããšã¿ãªãããŸãïŒ æ®ºãããçªç¶å€ç°äœ ïŒã çªç¶å€ç°åŸã«ãã¹ããæåããå Žåãããã¯ãã³ãŒãããã®å Žæã®ãã¹ãã§ãŸã£ããã«ããŒãããŠããªãããçªç¶å€ç°ããã©ã€ã³ãã«ããŒãããã¹ããéå¹ççã§ãããã³ãŒãã®ãã®ã»ã¯ã·ã§ã³ãååã«ãã¹ãããŠããªãããšã瀺ããŸãã ãã®ãããªçªç¶å€ç°äœã¯ãçåè
ïŒ SurvivedãEscaped Mutant ïŒãšåŒã°ããŸãã
çªç¶å€ç°ãã¹ãã¯æ··oticãšããã³ãŒã倿ã§ã¯ãªãã絶察ã«äºæž¬å¯èœã§çè§£å¯èœãªããã»ã¹ã§ãããåãå
¥åçªç¶å€ç°æŒç®åã䜿çšãããšããã¹ã察象ã®åããœãŒã¹ã³ãŒãã§åžžã«åãçªç¶å€ç°ãªã¹ããšçµæã®ã¡ããªãã¯ãçæãããããšãçè§£ããããšãéèŠã§ãã
äŸãèããŠã¿ãŸãããã PHP- Infectionã«ã¯Mutational FrameworkïŒMFïŒã䜿çšããŸãã
ãªããžã§ã¯ãæåã®ã¹ã¿ã€ã«ã§èšè¿°ãããã幎霢å¥ã«ãŠãŒã¶ãŒã®ã³ã¬ã¯ã·ã§ã³ãé€å€ã§ããäœããã®ãã£ã«ã¿ãŒããããšããŸãã
class UserFilterAge { const AGE_THRESHOLD = 18; public function __invoke(array $collection) { return array_filter( $collection, function (array $item) { return $item['age'] >= self::AGE_THRESHOLD; } ); } }
ãããŠããã®ãã£ã«ã¿ãŒã«ã¯åäœãã¹ãããããŸãïŒ
public function test_it_filters_adults() { $filter = new UserFilterAge(); $users = [ ['age' => 20], ['age' => 15], ]; $this->assertCount(1, $filter($users)); }
ãã®ãã¹ãã¯éåžžã«ç°¡åã§ãã2人ã®ãŠãŒã¶ãŒã远å ãããã£ã«ã¿ãŒã¯ãã®ãã¡ã®1人ïŒ20æ³ïŒã®ã¿ãè¿ãããšãæ³å®ããŠããŸãã
ãã®ãã¹ãã®ã¿ã䜿çšããå Žåã UserFilterAge
ã¯ã©ã¹ã®ãœãŒã¹ã³ãŒãã¯100ïŒ
æ¢ã«ã«ããŒãããŠããããšã«æ³šæããŠãã ããã çªç¶å€ç°ãã¹ããå®è¡ããçµæãåæããŸãã
./infection.phar --threads=4

ã³ãŒãã100ïŒ
ç¶²çŸ
ãããŠãããããMSIã®67ïŒ
ãããããŸãããããã¯ãã§ã«çããããã®ã§ãã
MSIã¯ã©ã®ããã«èæ
®ãããŸããïŒ Metrics: Mutation Score Indicator (MSI): 47% Mutation Code Coverage: 67% Covered Code MSI: 70%
çªç¶å€ç°ã¹ã³ã¢ã€ã³ãžã±ãŒã¿ãŒïŒMSIïŒ
MSIã¯47ïŒ
ã§ãã ããã¯ãçæããããã¹ãŠã®çªç¶å€ç°ã®47ïŒ
ãçãæ®ããªãã£ãããšãæå³ããŸãïŒåŒ·å¶çµäºãã¿ã€ã ã¢ãŠãããšã©ãŒïŒã MSIã¯ãå€ç°æ€æ»ã®äž»èŠãªææšã§ãã ã³ãŒãã«ãã¬ããžã65ïŒ
ã®å Žåãå·®ã¯18ïŒ
ã«ãªããŸããããã¯ããã®å Žåã®ã³ãŒãè¡ã®ã«ãã¬ããžã®å²åããã¹ããè©äŸ¡ããããã®äžååãªåºæºã§ããããšã瀺ããŠããŸãã
ã«ãŠã³ãåŒïŒ
TotalDefeatedMutants = KilledCount + TimedOutCount + ErrorCount; MSI = (TotalDefeatedMutants / TotalMutantsCount) * 100;
çªç¶å€ç°ã³ãŒãã®ç¯å²
ãã®ææšã¯67ïŒ
ã§ãã äžè¬ã«ãã³ãŒãã«ãã¬ããžã€ã³ãžã±ãŒã¿ãŒãšã»ãŒåãã§ããå¿
èŠããããŸãã
ã«ãŠã³ãåŒïŒ
TotalCoveredByTestsMutants = TotalMutantsCount - NotCoveredByTestsCount; CoveredRate = (TotalCoveredByTestsMutants / TotalMutantsCount) * 100;
察象ã³ãŒãçªç¶å€ç°ã¹ã³ã¢ã€ã³ãžã±ãŒã¿
ãã¹ãã§ã«ããŒãããã³ãŒãã®MSIã¯70ïŒ
ã§ãã ãã®åºæºã¯ããã¹ããå®éã«ã©ãã»ã©å¹æçãã瀺ããŸãã ã€ãŸããããã¯ããã¹ãã§ã«ããŒãããã³ãŒãã«å¯ŸããŠçæããããã¹ãŠã®æ®ºããããã¥ãŒã¿ã³ãã®å²åã§ãã
ã«ãŠã³ãåŒïŒ
TotalCoveredByTestsMutants = TotalMutantsCount - NotCoveredByTestsCount; TotalDefeatedMutants = KilledCount + TimedOutCount + ErrorCount; CoveredCodeMSI = (TotalDefeatedMutants / TotalCoveredByTestsMutants) * 100;
ã¡ããªãã¯ãåæãããšãMSIã¯ã³ãŒãã«ãã¬ããžã¡ããªãã¯ãã18ãŠãããå°ãªãããšãããããŸãã ããã¯ãçªç¶å€ç°ãã¹ãã®çµæã«ãããšã裞ã®ã³ãŒãã«ãã¬ããžã®çµæããããã¹ãã®å¹æãã¯ããã«äœãããšã瀺åããŠããŸãã
çæãããçªç¶å€ç°ãèŠãŠã¿ãŸãããã
æåã®çªç¶å€ç°ïŒ
class UserFilterAge { const AGE_THRESHOLD = 18; public function __invoke(array $collection) { return array_filter( $collection, function (array $item) { - return $item['age'] >= self::AGE_THRESHOLD; + return $item['age'] > self::AGE_THRESHOLD; } ); } }
ãã¹ãã®å®è¡ã¯æåããŸããã ã€ãŸãããœãŒã¹ã³ãŒãã倿ŽããŠãããã¹ãçµæã«ã¯ãŸã£ãã圱é¿ããããŸããã§ããã ããã¯å¿
èŠãªãã®ã§ã¯ãããŸããã
çªç¶å€ç°ãã¹ãã«ãããæ¡ä»¶ã">="
ãã">"
ã«çœ®ãæããŠçœ®ãæããããšãã§ããããã°ã©ã ãåæ§ã«æ©èœããããšãããããŸããã ãŠããããã¹ãã¯ãããã°ã©ã ãåžæã©ããã«æ©èœããããšãä¿èšŒããŸãã ãããŠããã®ãããªå€æŽãããã³ãŒãã§ãã¹ããæåããããããã®åäœãæåŸ
ãããŸãã
ãã®å€ç°ã¯ãééã®æ¡ä»¶ã§ã³ãŒãããã¹ããããšããåžžã«å¢çå€ããã§ãã¯ããå¿
èŠãããããšã瀺ããŠããŸãã
ç¶æ³ãä¿®æ£ããŠãã¥ãŒã¿ã³ããæ®ºããŸãããïŒ
public function test_it_filters_adults(array $users, int $expectedCount) { $filter = new UserFilterAge(); $this->assertCount($expectedCount, $filter($users)); } public function usersProvider() { return [ [ [ ['age' => 15], ['age' => 20], ], 1 ], [ [ ['age' => 18], ], 1 ] ]; }
å¢çå€ã«1ã€ã®ãã¹ã-18ã远å ããŸããã倿Žãããã³ãŒãã䜿çšããŠãã¹ããå床å®è¡ãããšããã¹ãŠã®å€ãé€å€ããã空ã®ã³ã¬ã¯ã·ã§ã³ãè¿ãããããããã¹ãã¯å€±æããŸãã
äºæ¬¡å€ç°ïŒ
class UserFilterAge { const AGE_THRESHOLD = 18; public function __invoke(array $collection) { - return array_filter( + array_filter( $collection, function (array $item) { return $item['age'] >= self::AGE_THRESHOLD; } ); + return null; } }
äœãèµ·ãã£ãã®ãããã«ã¯ããããŸããã ããã¯ã "return functionCall();"
ãšããåŒã®é¢æ°åŒã³åºãã眮ãæãããããªãè峿·±ãçªç¶å€ç°æŒç®å"return functionCall();"
on "functionCall(); return null;"
ã
ãããããªããã®ãããªçªç¶å€ç°ãèµ·ãã£ãã®ã§ããããïŒ ãã£ã«ã¿ãŒãããé
åãæåŸ
ããŠãããšãã«null
ãè¿ãã®ã¯æ¬åœã§ããïŒ ãã¡ãããããã¯çå®ã§ã¯ãããŸãããããã¯ã颿°ã§æ»ãå€ã®åãæå®ããªãã£ãããã«çºçããŸãã MFã¯ãæ»ãå€ãnull
ã§ããå¯èœæ§ãããããšãèªèãããããã¹ãªããããããšããŸãã ãã®ç¹ã§ææã¯éåžžã«è³¢æã§ããã颿°ãæ»ãå€ã®ç¹å®ã®ã¿ã€ãïŒäŸãã°ã int
ãªã©ã®nullable
ã§ã¯ãªãïŒãå«ãå Žåãã³ãŒãã¯å€åããŸããã ãã®å€ç°äœãåæãããšãtypehintã远å ããå¿
èŠããããšçµè«ä»ããããŸãã
- public function __invoke(array $collection) + public function __invoke(array $collection): array
ããã§ã¡ãœããã®ã·ã°ããã£ã¯å®å
šã«æç¢ºã«ãªããŸãã-é
åããã£ã«ã¿ãŒã«æž¡ããŸããé
åãå¿
èŠã§ãã
ããäžåºŠå®è¡ããŠãçµæã確èªããŸãã

æ»ãå€åã®è¿œå ã«ããçªç¶å€ç°ã®æ°ãæžå°ãããšäºæ³ããããã¹ãŠã®çªç¶å€ç°ã殺ãããŸãã ããã§ãã³ãŒãã«ãã¬ããž100ïŒ
ã ãã§ãªãããã¥ãŒããŒã·ã§ã³ã³ãŒãã«ãã¬ããž100ïŒ
ããããŸããããã¯ããã¹ãã®å質ã瀺ãããææšçãªåºæºã§ãã
ãã®åçŽãªäŸã¯ããã¹ãã§ã³ãŒãã100ïŒ
ç¶²çŸ
ããŠããå Žåã§ããçªç¶å€ç°ãã¹ãã§åé¡ãæããã«ã§ããã³ãŒããã100ïŒ
以äžãã«ããŒã§ããããšã瀺ããŠããŸãã
ãŸã 䟵å
¥ããŠããªãå Žåã¯ããã匷åãªçªç¶å€ç°æŒç®åã§ããPublicVisibility
ãšProtectedVisibility
PublicVisibility
ããŠãã ããã ãããã®æå³ã¯ãã¯ã©ã¹ã®åã¡ãœããã®ã¢ã¯ã»ã¹ä¿®é£Ÿåãpublic
ããprotected
ã«å€æŽãã protected
ããprivate
ã«å€æŽããããšã§ãïŒäžéšã®éæ³ãšæœè±¡ã¡ãœãããé€ãïŒã
ããã«ãããã¡ãœããã®ãªãŒãã³æ§ã®å¿
èŠæ§ã確èªã§ããŸãã ãã®ãããªçªç¶å€ç°äœãçåè
ã§ããããšã倿ããå Žåãã¯ã©ã¹ã®ãããªãã¯ã€ã³ã¿ãŒãã§ã€ã¹ãæžããããšãã§ããããããåé·ã§ãããšçµè«ä»ããããšãã§ããŸãã ãããŠã ProtectedVisibility
ãªãã¬ãŒã¿ãŒã®å Žåãçãæ®ã£ããã¥ãŒã¿ã³ãã¯ãã¡ãœãããprivateã«å€æŽããå¿
èŠãããã芪protected
ã¡ãœããã䜿çš/åå®çŸ©ããã¯ã©ã¹ã®åäžã®åå«ã¯ååšããªããšèšããŸãã
ããšãã°ã FosUserBundle
ç¥ãããŠããªãFosUserBundle
ã«å¯ŸããŠInfectionãå®è¡ãããšãå
¬éã¡ãœããisLegacy
ããã®å
¬éæ§ãæžããããšãã§ããŸãã
./infection.php --threads=4 --show-mutations --mutators=PublicVisibility,ProtectedVisibility

çãæ®ã£ãŠæ®ºããããã¥ãŒã¿ã³ãã®ãããã®2ã€ã®ã±ãŒã¹ã«å ããŠãä»ã®ãã®ããããŸãã ããšãã°ãã«ãŒãå
ã®ã«ãŠã³ã¿å€æ°ã®åé
æŒç®å"++"
ã"--"
ã«å€æŽãããšãã«ãŒããçµäºããªãããšããããŸãã ç¡éã«ãªããŸãã ãã¥ãŒããŒã·ã§ã³ãã¹ããã¬ãŒã ã¯ãŒã¯ã®ã¿ã¹ã¯ã¯ããã®ãããªç¶æ³ãæ£ããåŠçãããã¥ãŒã¿ã³ããç¹å¥ãªã¹ããŒã¿ã¹Timeoutã§ããŒã¯ããããšã§ãã ãã®çµæã¯éœæ§ã§ãããå€ç°äœã¯çåè
ãšã¯èŠãªãããŸããã
äžè¬ã«ãçè«ãçè§£ããŸãããæ¬¡ã«ãææã®è©³çްãšãPHPã®ãã®ä»ã®ä»£æ¿æ¹æ³ãèŠãŠã¿ãŸãããã
ææPHP
Infectionãæ©èœãããã«ã¯ãã³ãŒãã«ãã¬ããžãšPHP 7.0+çšã«ã€ã³ã¹ããŒã«ãããxDebugæ¡åŒµæ©èœãå¿
èŠã§ãã
æšå¥šãããã€ã³ã¹ããŒã«æ¹æ³ã¯ãèªåçã«æŽæ°ããæ©èœïŒ infection.phar self-update
ïŒã§ãPharã¢ãŒã«ã€ãã§ãã
çŸåšãPHPUnitïŒ5ã6+ïŒãšPhpSpecã®2ã€ã®ãã¹ããã¬ãŒã ã¯ãŒã¯ãæšæºã§ãµããŒããããŠããŸãã
æåã®èµ·åæã«ããããžã§ã¯ãã®ã«ãŒãããfection.json.distèšå®ãäœæãããVCSã«ã³ãããã§ããŸãã ãã¥ãŒããŒã·ã§ã³ãäŸå€ãã¿ã€ã ã¢ãŠãå€ãªã©ã®ãœãŒã¹ãã©ã«ããŒã瀺ããŸãã
ãã¥ãŒããŒã·ã§ã³ãã¹ãã¯å
šäœãšããŠäººéã®åæãå¿
èŠãšãããããMFæäœã®å®äºåŸãçæããããã¹ãŠã®ãã¥ãŒããŒã·ã§ã³ã¯åããã©ã«ããŒinfection-log.txt
ã®ãã°ãã¡ã€ã«ã«åé¡ãããŸãã
ãªãã·ã§ã³
Infectionãèµ·åãããæãè峿·±ããªãã·ã§ã³ã®ãã¡ã次ã®ãã®ãåºå¥ã§ããŸãã
--threads
ããã¯ãçæããããã¥ãŒã¿ã³ãã®ã»ããå
šäœãå®è¡ããããã«äžŠè¡ããŠå®è¡ãããã¹ã¬ããã®æ°ã§ãã å®è¡æéã倧å¹
ã«ççž®ããŸãã ãã ããæ³šæç¹ããããŸãããã¹ããäœããã®åœ¢ã§çžäºã«äŸåããŠããå ŽåããŸãã¯ããŒã¿ããŒã¹ã䜿çšããŠããå Žåããã®ãªãã·ã§ã³ã䜿çšãããšããã¹ãã倿°åé€ãããçµæã®ã¡ããªãã¯ã«æªåœ±é¿ãäžããå¯èœæ§ããããŸãã ãã®ãããå°ãªããšãå®è£
ã®åææ®µéã§ãã°ãåç
§ãã䟡å€ããããŸãã
--show-mutations
ã³ã³ãœãŒã«ã«éãã«ã®ãã¥ãŒã¿ã³ããšã®å·®åãããã«è¡šç€ºããŸããããã«ãããçµæãå³åº§ã«åæãããããæžããŠãããšãã«ãã¹ããä¿®æ£ã§ããŸãã
--mutators
ã³ãŒãã倿Žããçªç¶å€ç°æŒç®åã®åæã ããšãã°ãPublicVisibilityã¹ããŒãã¡ã³ããšProtectedVisibilityã¹ããŒãã¡ã³ãã®ã¿ããã§ãã¯ããå Žåã«äŸ¿å©ã§ãã
./infection.phar --mutators=PublicVisibility,ProtectedVisibility
--min-msi
ããã³--min-covered-msi
ããã2ã€ã®ãªãã·ã§ã³ã¯ãContinious IntegrationãµãŒããŒã§ãããžã§ã¯ããæ§ç¯ããããã»ã¹ã®ã¹ãããã®1ã€ãšããŠææãå®è¡ããå Žåã«åœ¹ç«ã¡ãŸãã
--min-msi
䜿çšãããšãçªç¶å€ç°ã¹ã³ã¢ã€ã³ãžã±ãŒã¿ãŒã®æå°å€ïŒããŒã»ã³ãåäœïŒãæå®ã§ããŸãã æå®ããå€ãå®éã®å€ãããå°ããå Žåããã«ãã¯å€±æããŸãã ãã®ãªãã·ã§ã³ã¯ãåãã«ãã§ããå€ãã®ã³ãŒãè¡ãã«ããŒããããã«åŒ·å¶ããŸãã
--min-covered-msi
䜿çšãããšãCovered Code MSIã®æå°å€ãããããæå®ã§ããŸãã åãã«ãã®ãã®ãªãã·ã§ã³ã«ãããããå¹ççã§ä¿¡é Œæ§ã®é«ããã¹ããäœæã§ããŸãã
äž¡æ¹ã®ãªãã·ã§ã³ãåå¥ã«ãŸãã¯äžç·ã«äœ¿çšã§ããŸãã
./infection.phar --min-msi=80 --min-covered-msi=95
Travis CIã§äœ¿çšãã
before_script: - wget https://github.com/infection/infection/releases/download/0.5.0/infection.phar - wget https://github.com/infection/infection/releases/download/0.5.0/infection.phar.pubkey - chmod +x infection.phar script: - ./infection.phar --min-covered-msi=90 --threads=4
åãªãªãŒã¹ïŒPharã¢ãŒã«ã€ãïŒã¯openssl
ç§å¯éµã§çœ²åãããŠãããããã¢ãŒã«ã€ãèªäœã«å ããŠãå
¬ééµãããŠã³ããŒãããå¿
èŠããããŸãã
å€ç°æ€æ»ã®äœ¿ç𿹿³ã¯ïŒ
ãã¥ãŒããŒã·ã§ã³ãã¹ãã¯ãä»äºãå人ãããžã§ã¯ãã§éçºè
ãšããŠã©ã®ããã«åœ¹ç«ã¡ãŸããïŒ æ¢åã®ãããžã§ã¯ãã«å®è£
ããæ¹æ³ã¯ïŒ
éçºè
ã®æ¯æ¥ã®äœ¿çš
çªç¶å€ç°ãã¹ãã¯ãæ°ãããã¹ããäœæãããšãã«æ¥åžžæ¥åã§åœ¹ç«ã¡ãŸãã äœæ¥ã¹ããŒã ã¯æ¬¡ã®ããã«ãªããŸãã
- äžèšã®äŸãšåã
UserFilterAge
ãªã©ãæ°ãã颿°ãUserFilterAge
ããŸãã - ãã®ã³ãŒãã¯ãã§ã«ãã¹ãã§ã«ããŒãããŠããŸã
- ãã¹ãããã¹ãããã«ã¯ããã®ãã¡ã€ã«ã«å¯ŸããŠã®ã¿ãã¥ãŒããŒã·ã§ã³ãã¹ããå®è¡ããŸã
./infection.phar --threads=4 --filter=UserFilterAge.php --show-mutations
çãæ®ã£ãå€ç°äœãåæããåªããCovered Code MSIã¹ã³ã¢ãéæããããšããŸãã ãã¹ãã§ã«ããŒãããã³ãŒãã«å¯ŸããŠçæããããã¹ãŠã®ãã¥ãŒã¿ã³ãã®æ®ºå®³ã®å²åã100ã«ãªãåŸåããããŸããããã«ããããã¹ããå¯èœãªéãå¹ççã«èšè¿°ã§ããŸãã
MTã䜿çšãããšãããå€ãã®ãã¹ãã§ããç°¡æœãªã³ãŒããèšè¿°ããããšã«æ°ä»ãã§ãããã ããã¯ãã³ãŒãè¡ã®éåžžã®ã«ãã¬ããž ïŒã©ã€ã³ã«ãã¬ããžïŒã®ä»£ããã«ãã³ãŒãã®ãã¹ãŠã®ãã¹ããã¹ãããããšãã«ãã©ã³ãã«ãã¬ããžã䜿çšããŸãã
ãããžã§ã¯ãã§ã®æ¯æ¥ã®äœ¿çš
çªç¶å€ç°ãã¹ãã¯ãContinious IntegrationãµãŒããŒã§äœ¿çšã§ããŸãã ãããžã§ã¯ãã®ãµã€ãºã«å¿ããŠããã«ãããšã«éå§ããããšãã1æ¥1åå€éã«ãªãã·ã§ã³ãšããŠéå§ããããšãã§ããŸãã ããã§ã®äž»ãªããšã¯ãçµæãåæãããã¹ãã®å質ãåžžã«æ¹åããããšã§ãã
ç§ã®æèŠã§ã¯ãã¬ããŒãã®ã¿ãçæããŠãããã©ãŒãã³ã¹ã¯åäžããªãããã --min-msi
ããã³/ãŸãã¯--min-covered-msi
ãªãã·ã§ã³ã䜿çšããããšãã--min-msi
ãŸãã
ããšãã°ãmutation Infectionãã¬ãŒã ã¯ãŒã¯ã¯ããã«ãããšã«èªèº«ãçªç¶å€ç°çã«ãã¹ãããŸã ã ãŸããææšãäœäžãããšããã«ããäœäžããŸãã
MTãçµ¶ãã䜿çšããããšã§ããããžã§ã¯ãã®MSIã€ã³ãžã±ãŒã¿ãŒã倧ãããªãããªãã·ã§ã³--min-msi
ããã³--min-covered-msi
ã®å€ãåŸã
ã«å¢ããããšãã§ããŸãã
100ïŒ
MSIãéæã§ããªãããšãããã®ã¯ãªãã§ããïŒ
çªç¶å€ç°è©Šéšã§ã¯ãåäžã®çªç¶å€ç°äœã®æŠå¿µããããŸãã ã€ãŸãããããã¯ãããžãã¯ã®ç¹ã§åäžã®ã³ãŒããããããçªç¶å€ç°ã§ãã ãã®ãããªçªç¶å€ç°ã®äŸã¯ã次ã®ã³ãŒãã§ãã
public function calculateExpectedValueAt(DateTimeInterface $date) { $diffInDays = (int) $this->startedAt->diff($date)->format('%a'); $multiplier = $this->initialValue < $this->targetValue ? 1 : -1; $initialAveragePerDay = $this->calculateInitialAveragePerDay(); - return $this->initialValue + ($initialAveragePerDay * $diffInDays * $multiplier); + return $this->initialValue + ($initialAveragePerDay * $diffInDays / $multiplier); }
ãã€ã³ãã¯ãæ°ãæããŠÂ±1
å²ããšåãçµæã«ãªãããã®ãããªçªç¶å€ç°äœã¯çãæ®ã£ãŠããããã«èŠãããšããããšã§ãã
ãã®ç¹ã§ãå®éã«ã¯ã³ãŒãå
šäœã§100ïŒ
ã®MSIãæåŸ
ããªãã§ãã ããã ãããè¡ãã«ã¯ãåäžã®å€ç°äœãç»é²ãã匷åãªã·ã¹ãã ãšãçµæã®ã¡ããªãã¯ããããããé€å€ããæ©èœãå¿
èŠã§ãã
PHPã®ä»£æ¿
PHPã§ã®ææã«å¯Ÿããå¯äžã®æ¬æ Œçãªä»£æ¿æ©èœã¯Humbugã§ããããã¯äžè¬ã«ãPHPã§æåã®MFã§ãã å©ç¹ã®ãã¡ããã¥ãŒããŒã·ã§ã³ã®ãã£ãã·ã¥ïŒå¢åãã£ãã·ã¥ïŒã®å®éšçãªãµããŒãããããŸãã ã€ãŸããäžéšã®ãã¡ã€ã«ã倿Žãããããã®è¡ãã«ããŒãããã¹ããæ¬¡ã®éå§æã«åé€ãããªãã£ãå Žåããã¥ãŒããŒã·ã§ã³ã¯éå§ããããæåŸã®å®è¡ã®çµæãååŸãããŸãã çè«çã«ã¯ãããã«ããäœæ¥é床ã倧å¹
ã«åäžããŸãããã¡ããªãã¯ã®èª€æ€ç¥ããšã©ãŒã«ã€ãªããå¯èœæ§ããããŸãã
äžæ¹ãHumbugã¯ãŸã PHPUnit 6+ããã³PhpSpecããµããŒãããŠããŸããã ãã ããçŸæç¹ã§ã®InfectionãšHumbugã®äž»ãªéãã¯ãInfectionã¯æœè±¡æ§æããªãŒã䜿çšããŠã³ãŒãã倿Žããããšã§ãïŒ Abstract Syntax TreeïŒASTïŒ ïŒã Nikita Popovã®æ³šç®ãã¹ããããžã§ã¯ãã PHP-Parserã®ãããã§ãASTãæ§ç¯ã§ããŸãã
ASTã䜿çšããçç±ã¯äœã§ããïŒ ããã«è©³ããèããŠã¿ãŸãããã
ã³ãŒãã®å€æŽãéå§ããã«ã¯ã以äžãè¡ãå¿
èŠããããŸã
- ãã¡ã€ã«ã³ãŒããããŒã¯ã³ã«åå²ãïŒ token_get_allïŒïŒé¢æ°ïŒãããããé
åã«å
¥ããŸã
- é
åãå®è¡ããå¿
èŠã«å¿ããŠãçªç¶å€ç°æŒç®åã«åŸã£ãŠåããŒã¯ã³ãå¥ã®ããŒã¯ã³ã«çœ®ãæããŸã
- ããŒã¯ã³ã®æ°ããã»ããããã倿Žãããæ°ãããœãŒã¹ã³ãŒããåéãã
ããŒã¯ã³ã®äŸ T_OPEN_TAG ('<?php ') T_BOOLEAN_AND ('&&') T_INC ('++') T_WHITESPACE (' ') ...
ããããå®éã«ã¯ããã»ã¹ã¯ã¯ããã«è€éã§ãã ããŒã¯ã³ã倿Žããæ±ºå®ã¯ãããã€ãã®æ¡ä»¶ã«äŸåããŸãã
- 颿°ã®æ¬äœã«ããŸããïŒ
T_OPEN_TAG ('<?php ')
眮ãæãã¯æå³ããããŸãã - çªç¶å€ç°åŸã®ã³ãŒãã¯æå¹ã§ããïŒ ïŒããšãã°ãé
å
['a'] + ['b']
远å ã¯æå¹ãªã³ãŒãã§ããããããé
å['a'] - ['b']
æžç®ã¯ãã§ã«èŽåœçãšã©ãŒã§ãããããã£ãŠããã®ãããªçªç¶å€ç°ã¯å¿
èŠãããŸããã MFã¯ã远å ããŒã¯ã³ãé
åéã«ãããã©ããã確èªããå¿
èŠããããŸãã
ãã®çµæãããŒã¯ã³ã®é
åã䜿çšããŠãã³ãŒãã®èгç¹ãããããã®è³ªåã«çããããšã¯ããªãå°é£ã§ãã ããã©ããããæœè±¡æ§æããªãŒãããããããœãŒã¹ã³ãŒãã衚ããªããžã§ã¯ãïŒ Node\Expr\BinaryOp\Plus
ã Node\Expr\BinaryOp\Minus
ã Node\Expr\Array_
ïŒãæäœããããšã§ç°¡åã«å®è¡ã§ããŸãã
é
åããã§ãã¯ããŠ"-"
"+"
ã"-"
ã«å€æŽãããçªç¶å€ç°æŒç®åã®å®è£
ã¯æ¬¡ã®ãšããã§ãã
ææç class Plus implements Mutator { public function mutate(Node $node) { return new BinaryOp\Minus($node->left, $node->right, $node->getAttributes()); } public function shouldMutate(Node $node) : bool { if (!($node instanceof BinaryOp\Plus)) { return false; } if ($node->left instanceof Array_ && $node->right instanceof Array_) { return false; } return true; } }
ãã³ãã° class Addition extends MutatorAbstract { public static function getMutation(array &$tokens, $index) { $tokens[$index] = '-'; } public static function mutates(array &$tokens, $index) { $t = $tokens[$index]; if (!is_array($t) && $t == '+') { $tokenCount = count($tokens); for ($i = $index + 1; $i < $tokenCount; $i++) {
æããã«ãASTã䜿çšãããšå€§ããªå©ç¹ããããŸãã äœæ¥ãç°¡åã«ãªããã³ãŒãã®ä¿å®ãšçè§£ã容æã«ãªããæ°ããçªç¶å€ç°æŒç®åã®äœæãç°¡åã«ãªããããªãŒã®æã«æ²¿ã£ãŠæ©ãã³ãŒãã®åæãç°¡åã«ãªããŸãã
äžè¬ã«ãçªç¶å€ç°ãã¹ãã¯ãã¹ããšã³ãŒãå
šäœã®å質ãåäžããããã1ã€ã®æ¹æ³ã§ãããæ³šæãã䟡å€ããããŸãã
å®éã®ãããžã§ã¯ãã§MTã䜿çšããçµéšãããå ŽåããŸãã¯ææã詊ã¿ãŠã³ãŒãå
ã®è峿·±ããšã©ãŒãèŠã€ããå Žåã¯ãæçšãªã±ãŒã¹ã«ã€ããŠã³ã¡ã³ãã§å
±æããŠãã ããã
䜿çšãããæç®ïŒ