рдЙрддреНрдкрд░рд┐рд╡рд░реНрддрди рдкрд░реАрдХреНрд╖рдг: рдкрд░реАрдХреНрд╖рдг рдкрд░реАрдХреНрд╖рдг


рд▓реЗрдЦрди рдкрд░реАрдХреНрд╖рдг рдХреЛ рдХреЛрдб рдХреЗ рд╕рд╣реА рд╕рдВрдЪрд╛рд▓рди рдореЗрдВ рдЖрддреНрдорд╡рд┐рд╢реНрд╡рд╛рд╕ рдХреЛ рдкреНрд░реЗрд░рд┐рдд рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдПред рдЕрдХреНрд╕рд░ рд╣рдо рдХреЛрдб рдХреЗ рдХрд╡рд░реЗрдЬ рдХреА рдбрд┐рдЧреНрд░реА рдкрд░ рдХрд╛рдо рдХрд░рддреЗ рд╣реИрдВ, рдФрд░ рдЬрдм рд╣рдо 100% рддрдХ рдкрд╣реБрдВрдЪрддреЗ рд╣реИрдВ, рддреЛ рд╣рдо рдХрд╣ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рд╕рдорд╛рдзрд╛рди рд╕рд╣реА рд╣реИред рдХреНрдпрд╛ рдЖрдк рдЗрд╕ рдмрд╛рд░реЗ рдореЗрдВ рдирд┐рд╢реНрдЪрд┐рдд рд╣реИрдВ? рд╢рд╛рдпрдж рдПрдХ рдЙрдкрдХрд░рдг рд╣реИ рдЬреЛ рдЕрдзрд┐рдХ рд╕рдЯреАрдХ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рджреЗрдЧрд╛?

рдЙрддреНрдкрд░рд┐рд╡рд░реНрддрди рдкрд░реАрдХреНрд╖рдг


рдпрд╣ рд╢рдмреНрдж рдПрдХ рдРрд╕реА рд╕реНрдерд┐рддрд┐ рдХрд╛ рд╡рд░реНрдгрди рдХрд░рддрд╛ рд╣реИ рдЬрд╣рд╛рдВ рд╣рдо рдХреЛрдб рдХреЗ рдЫреЛрдЯреЗ рдЯреБрдХрдбрд╝реЛрдВ рдХреЛ рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рджреЗрдЦрддреЗ рд╣реИрдВ рдХрд┐ рдпрд╣ рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЛ рдХреИрд╕реЗ рдкреНрд░рднрд╛рд╡рд┐рдд рдХрд░рддрд╛ рд╣реИред рдпрджрд┐, рдкрд░рд┐рд╡рд░реНрддрдиреЛрдВ рдХреЗ рдмрд╛рдж, рдкрд░реАрдХреНрд╖рдг рд╕рд╣реА рдврдВрдЧ рд╕реЗ рдХрд┐рдП рдЬрд╛рддреЗ рд╣реИрдВ, рддреЛ рдпрд╣ рдЗрдВрдЧрд┐рдд рдХрд░рддрд╛ рд╣реИ рдХрд┐ рдХреЛрдб рдХреЗ рдЗрди рдЯреБрдХрдбрд╝реЛрдВ рдХреЗ рд▓рд┐рдП рдкрд░реАрдХреНрд╖рдг рдкрд░реНрдпрд╛рдкреНрдд рдирд╣реАрдВ рд╣реИрдВред рдмреЗрд╢рдХ, рдпрд╣ рд╕рдм рдЗрд╕ рдмрд╛рдд рдкрд░ рдирд┐рд░реНрднрд░ рдХрд░рддрд╛ рд╣реИ рдХрд┐ рд╣рдо рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдХреНрдпрд╛ рдмрджрд▓ рд░рд╣реЗ рд╣реИрдВ, рдХреНрдпреЛрдВрдХрд┐ рд╣рдореЗрдВ рд╕рднреА рд╕рдмрд╕реЗ рдЫреЛрдЯреЗ рдкрд░рд┐рд╡рд░реНрддрдиреЛрдВ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдЗрдВрдбреЗрдВрдЯ рдпрд╛ рдЪрд░ рдирд╛рдо, рдХреНрдпреЛрдВрдХрд┐ рдЙрдирдХреЗ рдмрд╛рдж рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЛ рднреА рд╕рд╣реА рдврдВрдЧ рд╕реЗ рдкреВрд░рд╛ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рдЗрд╕рд▓рд┐рдП, рдЙрддреНрдкрд░рд┐рд╡рд░реНрддрди рдкрд░реАрдХреНрд╖рдгреЛрдВ рдореЗрдВ, рд╣рдо рддрдерд╛рдХрдерд┐рдд рдореНрдпреВрдЯреЗрдЯрд░ (рд╕рдВрд╢реЛрдзрдХ рд╡рд┐рдзрд┐рдпреЛрдВ) рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ, рдЬреЛ рдПрдХ рдХреЛрдб рдХреЛ рджреВрд╕рд░реЗ рдХреЗ рд╕рд╛рде рдмрджрд▓ рджреЗрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдПрдХ рддрд░рд╣ рд╕реЗ рдЬреЛ рд╕рдордЭ рдореЗрдВ рдЖрддрд╛ рд╣реИред рд╣рдо рдЗрд╕ рдмрд╛рд░реЗ рдореЗрдВ рдиреАрдЪреЗ рдФрд░ рдЕрдзрд┐рдХ рд╡рд┐рд╕реНрддрд╛рд░ рд╕реЗ рдмрд╛рдд рдХрд░реЗрдВрдЧреЗред рдХрднреА-рдХрднреА рд╣рдо рдЦреБрдж рдРрд╕реЗ рдкрд░реАрдХреНрд╖рдг рдХрд░рддреЗ рд╣реИрдВ, рдЕрдЧрд░ рд╣рдо рдХреЛрдб рдореЗрдВ рдХреБрдЫ рдмрджрд▓рддреЗ рд╣реИрдВ рддреЛ рдЬрд╛рдБрдЪ рдЯреВрдЯ рдЬрд╛рддреА рд╣реИред рдпрджрд┐ рд╣рдордиреЗ "рдЖрдзрд╛ рд╕рд┐рд╕реНрдЯрдо" рдХреЛ рд░рд┐рдлреИрдХреНрдЯ рдХрд┐рдпрд╛ рдФрд░ рдкрд░реАрдХреНрд╖рдг рдЕрднреА рднреА рд╣рд░реЗ рд╣реИрдВ, рддреЛ рд╣рдо рддреБрд░рдВрдд рдХрд╣ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рд╡реЗ рдЦрд░рд╛рдм рд╣реИрдВред рдФрд░ рдЕрдЧрд░ рдХрд┐рд╕реА рдиреЗ рдРрд╕рд╛ рдХрд┐рдпрд╛ рдФрд░ рдкрд░реАрдХреНрд╖рдг рдЕрдЪреНрдЫреЗ рдереЗ, рддреЛ рдмрдзрд╛рдИ!

рд╕рдВрдХреНрд░рд╛рдордХ рдлреНрд░реЗрдорд╡рд░реНрдХ


рдЖрдЬ PHP рдореЗрдВ, рд╕рдмрд╕реЗ рд▓реЛрдХрдкреНрд░рд┐рдп рдЙрддреНрдкрд░рд┐рд╡рд░реНрддрди рдкрд░реАрдХреНрд╖рдг рдврд╛рдВрдЪрд╛ рд╕рдВрдХреНрд░рдордг рд╣реИ ред рдпрд╣ PHPUnit рдФрд░ PHPSpec рдХрд╛ рд╕рдорд░реНрдерди рдХрд░рддрд╛ рд╣реИ, рдФрд░ рдЗрд╕рдХреЗ рд▓рд┐рдП рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП PHP 7.1+ рдФрд░ Xdebug рдпрд╛ phpdbg рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред

рдкрд╣рд▓рд╛ рд▓реЙрдиреНрдЪ рдФрд░ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди


рдкрд╣рд▓реА рд╢реБрд░реБрдЖрдд рдореЗрдВ, рд╣рдо рдлреНрд░реЗрдорд╡рд░реНрдХ рдХреЗ рдЗрдВрдЯрд░реИрдХреНрдЯрд┐рд╡ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдХреЛ рджреЗрдЦрддреЗ рд╣реИрдВ, рдЬреЛ рд╕реЗрдЯрд┐рдВрдЧреНрд╕ рдХреЗ рд╕рд╛рде рдПрдХ рд╡рд┐рд╢реЗрд╖ рдлрд╝рд╛рдЗрд▓ рдмрдирд╛рддрд╛ рд╣реИ - рд╕рдВрдХреНрд░рдордгред json.distред рдпрд╣ рдХреБрдЫ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрддрд╛ рд╣реИ:

{ "timeout": 10, "source": { "directories": [ "src" }, "logs": { "text": "infection.log", "perMutator": "per-mutator.md" }, "mutators": { "@default": true } 

Timeout - рдПрдХ рд╡рд┐рдХрд▓реНрдк рдЬрд┐рд╕рдХрд╛ рдореВрд▓реНрдп рдПрдХ рдкрд░реАрдХреНрд╖рдг рдХреА рдЕрдзрд┐рдХрддрдо рдЕрд╡рдзрд┐ рдХреЗ рдмрд░рд╛рдмрд░ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдПред source рд╣рдо рдЙрди рдирд┐рд░реНрджреЗрд╢рд┐рдХрд╛рдУрдВ рдХреЛ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рддреЗ рд╣реИрдВ рдЬрд┐рдирд╕реЗ рд╣рдо рдХреЛрдб рдХреЛ рдореНрдпреВрдЯ рдХрд░реЗрдВрдЧреЗ, рдЖрдк рдЕрдкрд╡рд╛рдж рд╕реЗрдЯ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред logs рдореЗрдВ рдПрдХ text рд╡рд┐рдХрд▓реНрдк рд╣реЛрддрд╛ рд╣реИ, рдЬрд┐рд╕реЗ рд╣рдо рдХреЗрд╡рд▓ рдЧрд▓рдд рдкрд░реАрдХреНрд╖рдгреЛрдВ рдкрд░ рдЖрдБрдХрдбрд╝реЗ рдПрдХрддреНрд░ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░рддреЗ рд╣реИрдВ, рдЬреЛ рд╣рдорд╛рд░реЗ рд▓рд┐рдП рд╕рдмрд╕реЗ рджрд┐рд▓рдЪрд╕реНрдк рд╣реИред perMutator рд╡рд┐рдХрд▓реНрдк рдЖрдкрдХреЛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд┐рдП рдЧрдП perMutator рдХреЛ рдмрдЪрд╛рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред рдкреНрд░рд▓реЗрдЦрди рдореЗрдВ рдЗрд╕рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдФрд░ рдкрдврд╝реЗрдВред

рдЙрджрд╛рд╣рд░рдг


 final class Calculator { public function add(int $a, int $b): int { return $a + $b; } } 

рдорд╛рди рд▓реАрдЬрд┐рдП рдХрд┐ рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдЙрдкрд░реЛрдХреНрдд рд╡рд░реНрдЧ рд╣реИред рдЖрдЗрдП PHPUnit рдореЗрдВ рдПрдХ рдкрд░реАрдХреНрд╖рдг рд▓рд┐рдЦреЗрдВ:

 final class CalculatorTest extends TestCase { /** * @var Calculator */ private $calculator; public function setUp(): void { $this->calculator = new Calculator(); } /** * @dataProvider additionProvider */ public function testAdd(int $a, int $b, int $expected): void { $this->assertEquals($expected, $this->calculator->add($a, $b)); } public function additionProvider(): array { return [ [0, 0, 0], [6, 4, 10], [-1, -2, -3], [-2, 2, 0] ]; } } 

рдмреЗрд╢рдХ, рдпрд╣ рдкрд░реАрдХреНрд╖рдг add() рдкрджреНрдзрддрд┐ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рд╕реЗ рдкрд╣рд▓реЗ рд▓рд┐рдЦрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред рдЬрдм рдирд┐рд╖реНрдкрд╛рджрд┐рдд рд╣реЛ ./vendor/bin/phpunit рд╣рдореЗрдВ рдорд┐рд▓рддрд╛ рд╣реИ:

 PHPUnit 8.2.2 by Sebastian Bergmann and contributors. .... 4 / 4 (100%) Time: 39 ms, Memory: 4.00 MB OK (4 tests, 4 assertions) 

рдЕрдм рдЪрд▓рд╛рдПрдВ ./vendor/bin/infection :

 You are running Infection with Xdebug enabled. ____ ____ __ _ / _/___ / __/__ _____/ /_(_)___ ____ / // __ \/ /_/ _ \/ ___/ __/ / __ \/ __ \ _/ // / / / __/ __/ /__/ /_/ / /_/ / / / / /___/_/ /_/_/ \___/\___/\__/_/\____/_/ /_/ Running initial test suite... PHPUnit version: 8.2.2 9 [============================] 1 sec Generate mutants... Processing source code files: 1/1Creating mutated files and processes: 0/2 Creating mutated files and processes: 2/2 .: killed, M: escaped, S: uncovered, E: fatal error, T: timed out .. (2 / 2) 2 mutations were generated: 2 mutants were killed 0 mutants were not covered by tests 0 covered mutants were not detected 0 errors were encountered 0 time outs were encountered Metrics: Mutation Score Indicator (MSI): 100% Mutation Code Coverage: 100% Covered Code MSI: 100% Please note that some mutants will inevitably be harmless (ie false positives). Time: 1s. Memory: 10.00MB 

рд╕рдВрдХреНрд░рдордг рдХреЗ рдЕрдиреБрд╕рд╛рд░, рд╣рдорд╛рд░реЗ рдкрд░реАрдХреНрд╖рдг рд╕рдЯреАрдХ рд╣реИрдВред Per-mutator.md рдлрд╝рд╛рдЗрд▓ рдореЗрдВ , рд╣рдо рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рдХреНрдпрд╛ рдореНрдпреВрдЯреЗрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛:

 # Effects per Mutator | Mutator | Mutations | Killed | Escaped | Errors | Timed Out | MSI | Covered MSI | | ------- | --------- | ------ | ------- |------- | --------- | --- | ----------- | | Plus | 1 | 1 | 0 | 0 | 0 | 100| 100| | PublicVisibility | 1 | 1 | 0 | 0 | 0 | 100| 100| 

рдореНрдпреВрдЯреЗрдЯрд░ рдкреНрд▓рд╕ рдкреНрд▓рд╕ рд╕реЗ рдорд╛рдЗрдирд╕ рддрдХ рд╕рд╛рдЗрди рдХрд╛ рдПрдХ рд╕рд░рд▓ рдкрд░рд┐рд╡рд░реНрддрди рд╣реИ, рдЬреЛ рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЛ рддреЛрдбрд╝рдирд╛ рдЪрд╛рд╣рд┐рдПред рдФрд░ PublicVisibility рдЙрддреНрдкрд░рд┐рд╡рд░реНрддреА рдЗрд╕ рдкрджреНрдзрддрд┐ рдХреЗ рдПрдХреНрд╕реЗрд╕ рд╕рдВрд╢реЛрдзрдХ рдХреЛ рдмрджрд▓ рджреЗрддрд╛ рд╣реИ, рдЬрд┐рд╕реЗ рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЛ рднреА рддреЛрдбрд╝рдирд╛ рдЪрд╛рд╣рд┐рдП, рдФрд░ рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ рдпрд╣ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИред

рдЕрдм рдПрдХ рдФрд░ рдЕрдзрд┐рдХ рдЬрдЯрд┐рд▓ рд╡рд┐рдзрд┐ рдЬреЛрдбрд╝рддреЗ рд╣реИрдВред

 /** * @param int[] $numbers */ public function findGreaterThan(array $numbers, int $threshold): array { return \array_values(\array_filter($numbers, static function (int $number) use ($threshold) { return $number > $threshold; })); } /** * @dataProvider findGreaterThanProvider */ public function testFindGreaterThan(array $numbers, int $threshold, array $expected): void { $this->assertEquals($expected, $this->calculator->findGreaterThan($numbers, $threshold)); } public function findGreaterThanProvider(): array { return [ [[1, 2, 3], -1, [1, 2, 3]], [[-2, -3, -4], 0, []] ]; } 

рдирд┐рд╖реНрдкрд╛рджрди рдХреЗ рдмрд╛рдж, рд╣рдо рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдкрд░рд┐рдгрд╛рдо рджреЗрдЦреЗрдВрдЧреЗ:

 You are running Infection with Xdebug enabled. ____ ____ __ _ / _/___ / __/__ _____/ /_(_)___ ____ / // __ \/ /_/ _ \/ ___/ __/ / __ \/ __ \ _/ // / / / __/ __/ /__/ /_/ / /_/ / / / / /___/_/ /_/_/ \___/\___/\__/_/\____/_/ /_/ Running initial test suite... PHPUnit version: 8.2.2 11 [============================] < 1 sec Generate mutants... Processing source code files: 1/1Creating mutated files and processes: 0/7 Creating mutated files and processes: 7/7 .: killed, M: escaped, S: uncovered, E: fatal error, T: timed out ..M..M. (7 / 7) 7 mutations were generated: 5 mutants were killed 0 mutants were not covered by tests 2 covered mutants were not detected 0 errors were encountered 0 time outs were encountered Metrics: Mutation Score Indicator (MSI): 71% Mutation Code Coverage: 100% Covered Code MSI: 71% Please note that some mutants will inevitably be harmless (ie false positives). Time: 1s. Memory: 10.00MB 

рд╣рдорд╛рд░реЗ рдкрд░реАрдХреНрд╖рдг рдареАрдХ рдирд╣реАрдВ рд╣реИрдВред рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, infection.log рдлрд╝рд╛рдЗрд▓ рдХреА рдЬрд╛рдБрдЪ рдХрд░реЗрдВ:

 Escaped mutants: ================ 1) /home/sarven/projects/infection-playground/infection-playground/src/Calculator.php:19 [M] UnwrapArrayValues --- Original +++ New @@ @@ */ public function findGreaterThan(array $numbers, int $threshold) : array { - return \array_values(\array_filter($numbers, static function (int $number) use($threshold) { + return \array_filter($numbers, static function (int $number) use($threshold) { return $number > $threshold; - })); + }); } 2) /home/sarven/projects/infection-playground/infection-playground/src/Calculator.php:20 [M] GreaterThan --- Original +++ New @@ @@ public function findGreaterThan(array $numbers, int $threshold) : array { return \array_values(\array_filter($numbers, static function (int $number) use($threshold) { - return $number > $threshold; + return $number >= $threshold; })); } Timed Out mutants: ================== Not Covered mutants: ==================== 

рдкрд╣рд▓реА рдЕрдирд╕реБрд▓рдЭреА рд╕рдорд╕реНрдпрд╛ array_values рдлрд╝рдВрдХреНрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рд╣реИред рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХреБрдВрдЬрд┐рдпреЛрдВ рдХреЛ рд░реАрд╕реЗрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ array_filter рдорд╛рдиреЛрдВ рдХреЛ рдкрд┐рдЫрд▓реА рд╕рд░рдгреА рд╕реЗ рдХреБрдВрдЬрд┐рдпреЛрдВ рдХреЗ рд╕рд╛рде рд▓реМрдЯрд╛рддрд╛ рд╣реИред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рд╣рдорд╛рд░реЗ рдкрд░реАрдХреНрд╖рдгреЛрдВ рдореЗрдВ рдРрд╕рд╛ рдХреЛрдИ рдорд╛рдорд▓рд╛ рдирд╣реАрдВ рд╣реИ рдЬрдм array_values рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рдЖрд╡рд╢реНрдпрдХ рд╣реЛ, рдХреНрдпреЛрдВрдХрд┐ рдЕрдиреНрдпрдерд╛ рдПрдХ рд╣реА рдорд╛рди рдХреЗ рд╕рд╛рде рдПрдХ рд╕рд░рдгреА рд▓реЗрдХрд┐рди рд╡рд┐рднрд┐рдиреНрди рдХреБрдВрдЬрд┐рдпреЛрдВ рдХреЛ рд╡рд╛рдкрд╕ рдХрд░ рджрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред

рджреВрд╕рд░реА рд╕рдорд╕реНрдпрд╛ рд╕реАрдорд╛рд╡рд░реНрддреА рдорд╛рдорд▓реЛрдВ рд╕реЗ рд╕рдВрдмрдВрдзрд┐рдд рд╣реИред рддреБрд▓рдирд╛ рдореЗрдВ, рд╣рдордиреЗ > рд╕рд╛рдЗрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛, рд▓реЗрдХрд┐рди рд╣рдо рдХрд┐рд╕реА рднреА рд╕реАрдорд╛ рдХреЗ рдорд╛рдорд▓реЛрдВ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдирд╣реАрдВ рдХрд░рддреЗ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП >= рд╕рд╛рде рдкреНрд░рддрд┐рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдиреЗ рд╕реЗ рдкрд░реАрдХреНрд╖рдг рдирд╣реАрдВ рдЯреВрдЯрддреЗ рд╣реИрдВред рдЖрдкрдХреЛ рдХреЗрд╡рд▓ рдПрдХ рдкрд░реАрдХреНрд╖рдг рдЬреЛрдбрд╝рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ:

 public function findGreaterThanProvider(): array { return [ [[1, 2, 3], -1, [1, 2, 3]], [[-2, -3, -4], 0, []], [[4, 5, 6], 4, [5, 6]] ]; } 

рдФрд░ рдЕрдм рд╕рдВрдХреНрд░рдордг рд╕рдм рдХреБрдЫ рд╕реЗ рдЦреБрд╢ рд╣реИ:

 You are running Infection with Xdebug enabled. ____ ____ __ _ / _/___ / __/__ _____/ /_(_)___ ____ / // __ \/ /_/ _ \/ ___/ __/ / __ \/ __ \ _/ // / / / __/ __/ /__/ /_/ / /_/ / / / / /___/_/ /_/_/ \___/\___/\__/_/\____/_/ /_/ Running initial test suite... PHPUnit version: 8.2.2 12 [============================] < 1 sec Generate mutants... Processing source code files: 1/1Creating mutated files and processes: 0/7 Creating mutated files and processes: 7/7 .: killed, M: escaped, S: uncovered, E: fatal error, T: timed out ....... (7 / 7) 7 mutations were generated: 7 mutants were killed 0 mutants were not covered by tests 0 covered mutants were not detected 0 errors were encountered 0 time outs were encountered Metrics: Mutation Score Indicator (MSI): 100% Mutation Code Coverage: 100% Covered Code MSI: 100% Please note that some mutants will inevitably be harmless (ie false positives). Time: 1s. Memory: 10.00MB 

Calculator рд╡рд░реНрдЧ рдореЗрдВ рдПрдХ subtract рд╡рд┐рдзрд┐ рдЬреЛрдбрд╝реЗрдВ, рд▓реЗрдХрд┐рди PHPUnit рдореЗрдВ рдПрдХ рдЕрд▓рдЧ рдкрд░реАрдХреНрд╖рдг рдХреЗ рдмрд┐рдирд╛:

 public function subtract(int $a, int $b): int { return $a - $b; } 

рдФрд░ рдЗрдиреНрдлреЗрдХреНрд╢рди рдЪрд▓рдиреЗ рдХреЗ рдмрд╛рдж рд╣рдо рджреЗрдЦрддреЗ рд╣реИрдВ:

 You are running Infection with Xdebug enabled. ____ ____ __ _ / _/___ / __/__ _____/ /_(_)___ ____ / // __ \/ /_/ _ \/ ___/ __/ / __ \/ __ \ _/ // / / / __/ __/ /__/ /_/ / /_/ / / / / /___/_/ /_/_/ \___/\___/\__/_/\____/_/ /_/ Running initial test suite... PHPUnit version: 8.2.2 11 [============================] < 1 sec Generate mutants... Processing source code files: 1/1Creating mutated files and processes: 0/9 Creating mutated files and processes: 9/9 .: killed, M: escaped, S: uncovered, E: fatal error, T: timed out .......SS (9 / 9) 9 mutations were generated: 7 mutants were killed 2 mutants were not covered by tests 0 covered mutants were not detected 0 errors were encountered 0 time outs were encountered Metrics: Mutation Score Indicator (MSI): 77% Mutation Code Coverage: 77% Covered Code MSI: 100% Please note that some mutants will inevitably be harmless (ie false positives). Time: 1s. Memory: 10.00MB 

рдЗрд╕ рдмрд╛рд░, рдЯреВрд▓ рдиреЗ рджреЛ рдЦреБрд▓рд╛ рдореНрдпреВрдЯреЗрд╢рди рд▓реМрдЯрд╛ рджрд┐рдПред

 Escaped mutants: ================ Timed Out mutants: ================== Not Covered mutants: ==================== 1) /home/sarven/projects/infection-playground/infection-playground/src/Calculator.php:24 [M] PublicVisibility --- Original +++ New @@ @@ return $number > $threshold; })); } - public function subtract(int $a, int $b) : int + protected function subtract(int $a, int $b) : int { return $a - $b; } 2) /home/sarven/projects/infection-playground/infection-playground/src/Calculator.php:26 [M] Minus --- Original +++ New @@ @@ public function subtract(int $a, int $b) : int { - return $a - $b; + return $a + $b; } 

рдореИрдЯреНрд░рд┐рдХреНрд╕


рдкреНрд░рддреНрдпреЗрдХ рдирд┐рд╖реНрдкрд╛рджрди рдХреЗ рдмрд╛рдж, рдЙрдкрдХрд░рдг рддреАрди рдореИрдЯреНрд░рд┐рдХреНрд╕ рд▓реМрдЯрд╛рддрд╛ рд╣реИ:

 Metrics: Mutation Score Indicator (MSI): 47% Mutation Code Coverage: 67% Covered Code MSI: 70% 

Mutation Score Indicator - рдкрд░реАрдХреНрд╖рдгреЛрдВ рджреНрд╡рд╛рд░рд╛ рдкрддрд╛ рдЪрд▓рд╛ рдореНрдпреВрдЯреЗрд╢рди рдХрд╛ рдкреНрд░рддрд┐рд╢рддред

рдореАрдЯреНрд░рд┐рдХ рдХреА рдЧрдгрдирд╛ рдЗрд╕ рдкреНрд░рдХрд╛рд░ рдХреА рдЬрд╛рддреА рд╣реИ:

 TotalDefeatedMutants = KilledCount + TimedOutCount + ErrorCount; MSI = (TotalDefeatedMutants / TotalMutantsCount) * 100; 

Mutation Code Coverage - рдЙрддреНрдкрд░рд┐рд╡рд░реНрддрди рджреНрд╡рд╛рд░рд╛ рдХрд╡рд░ рдХреЛрдб рдХрд╛ рдЕрдиреБрдкрд╛рддред

рдореАрдЯреНрд░рд┐рдХ рдХреА рдЧрдгрдирд╛ рдЗрд╕ рдкреНрд░рдХрд╛рд░ рдХреА рдЬрд╛рддреА рд╣реИ:

 TotalCoveredByTestsMutants = TotalMutantsCount - NotCoveredByTestsCount; CoveredRate = (TotalCoveredByTestsMutants / TotalMutantsCount) * 100; 

Covered Code Mutation Score Indicator - рдкрд░реАрдХреНрд╖рдгреЛрдВ рджреНрд╡рд╛рд░рд╛ рдХрд╡рд░ рдХрд┐рдП рдЧрдП рдХреЛрдб рдХреЗ рд▓рд┐рдП рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреА рдкреНрд░рднрд╛рд╡рд╢реАрд▓рддрд╛ рдХреЛ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░рддрд╛ рд╣реИред

рдореАрдЯреНрд░рд┐рдХ рдХреА рдЧрдгрдирд╛ рдЗрд╕ рдкреНрд░рдХрд╛рд░ рдХреА рдЬрд╛рддреА рд╣реИ:

 TotalCoveredByTestsMutants = TotalMutantsCount - NotCoveredByTestsCount; TotalDefeatedMutants = KilledCount + TimedOutCount + ErrorCount; CoveredCodeMSI = (TotalDefeatedMutants / TotalCoveredByTestsMutants) * 100; 

рдЕрдзрд┐рдХ рдЬрдЯрд┐рд▓ рдкрд░рд┐рдпреЛрдЬрдирд╛рдУрдВ рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ


рдЙрдкрд░реЛрдХреНрдд рдЙрджрд╛рд╣рд░рдг рдореЗрдВ, рдХреЗрд╡рд▓ рдПрдХ рд╣реА рд╡рд░реНрдЧ рд╣реИ, рдЗрд╕рд▓рд┐рдП рд╣рдордиреЗ рдмрд┐рдирд╛ рдорд╛рдкрджрдВрдбреЛрдВ рдХреЗ рд╕рдВрдХреНрд░рдордг рдЪрд▓рд╛рдпрд╛ред рд▓реЗрдХрд┐рди рд╕рд╛рдзрд╛рд░рдг рдкрд░рд┐рдпреЛрдЬрдирд╛рдУрдВ рдкрд░ рд░реЛрдЬрдорд░реНрд░рд╛ рдХреЗ рдХрд╛рдо рдореЗрдВ тАУfilter рдкреИрд░рд╛рдореАрдЯрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рдЙрдкрдпреЛрдЧреА рд╣реЛрдЧрд╛, рдЬреЛ рдЖрдкрдХреЛ рдЙрди рдлрд╛рдЗрд▓реЛрдВ рдХреЗ рд╕реЗрдЯ рдХреЛ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ, рдЬрд┐рди рдкрд░ рд╣рдо рдореНрдпреВрдЯреЗрд╢рди рд▓рд╛рдЧреВ рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВред

 ./vendor/bin/infection --filter=Calculator.php 

рдЭреВрдареА рд╕рдХрд╛рд░рд╛рддреНрдордХрддрд╛


рдХреБрдЫ рдЙрддреНрдкрд░рд┐рд╡рд░реНрддрди рдХреЛрдб рдкреНрд░рджрд░реНрд╢рди рдХреЛ рдкреНрд░рднрд╛рд╡рд┐рдд рдирд╣реАрдВ рдХрд░рддреЗ рд╣реИрдВ, рдФрд░ рд╕рдВрдХреНрд░рдордг 100% рд╕реЗ рдХрдо рдПрдордПрд╕рдЖрдИ рджреЗрддрд╛ рд╣реИред рд▓реЗрдХрд┐рди рд╣рдо рд╣рдореЗрд╢рд╛ рдЗрд╕рдХреЗ рд╕рд╛рде рдХреБрдЫ рдирд╣реАрдВ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рд╣рдореЗрдВ рдРрд╕реА рд╕реНрдерд┐рддрд┐рдпреЛрдВ рдХреЗ рд╕рд╛рде рдЖрдирд╛ рд╣реЛрдЧрд╛ред рдРрд╕рд╛ рд╣реА рдХреБрдЫ рдЗрд╕ рдЙрджрд╛рд╣рд░рдг рдореЗрдВ рджрд┐рдЦрд╛рдпрд╛ рдЧрдпрд╛ рд╣реИ:

 public function calcNumber(int $a): int { return $a / $this->getRatio(); } private function getRatio(): int { return 1; } 

рдмреЗрд╢рдХ, рдпрд╣рд╛рдБ getRatio рдкрджреНрдзрддрд┐ рдХрд╛ getRatio рдирд╣реАрдВ рд╣реИ, рдПрдХ рд╕рд╛рдорд╛рдиреНрдп рдкрд░рд┐рдпреЛрдЬрдирд╛ рдореЗрдВ, рд╕рдВрднрд╡рддрдГ рдЗрд╕рдХреЗ рдмрдЬрд╛рдп рдХрд┐рд╕реА рдкреНрд░рдХрд╛рд░ рдХреА рдЧрдгрдирд╛ рд╣реЛрдЧреАред рд▓реЗрдХрд┐рди рдкрд░рд┐рдгрд╛рдо 1 рд╣реЛ рд╕рдХрддрд╛ рд╣реИред рд╕рдВрдХреНрд░рдордг рд░рд┐рдЯрд░реНрди:

 Escaped mutants: ================ 1) /home/sarven/projects/infection-playground/infection-playground/src/Calculator.php:26 [M] Division --- Original +++ New @@ @@ public function calcNumber(int $a) : int { - return $a / $this->getRatio(); + return $a * $this->getRatio(); } private function getRatio() : int 

рдЬреИрд╕рд╛ рдХрд┐ рд╣рдо рдЬрд╛рдирддреЗ рд╣реИрдВ, рдореВрд▓ рд╕рдВрдЦреНрдпрд╛ рдХреЗ рдмрд░рд╛рдмрд░, 1 рд╕реЗ рдЧреБрдгрд╛ рдХрд░рдирд╛ рдФрд░ рд╡рд┐рднрд╛рдЬрд┐рдд рдХрд░рдирд╛ рдПрдХ рд╣реА рдкрд░рд┐рдгрд╛рдо рджреЗрддрд╛ рд╣реИред рддреЛ рдпрд╣ рдЙрддреНрдкрд░рд┐рд╡рд░реНрддрди рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЛ рдирд╣реАрдВ рддреЛрдбрд╝рдирд╛ рдЪрд╛рд╣рд┐рдП, рдФрд░ рд╣рдорд╛рд░реЗ рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреА рд╕рдЯреАрдХрддрд╛ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рд╕рдВрдХреНрд░рдордг рдХреЗ рд╡рд┐рдЪреНрдЫреЗрджрди рдХреЗ рдмрд╛рд╡рдЬреВрдж, рд╕рдм рдХреБрдЫ рдХреНрд░рдо рдореЗрдВ рд╣реИред

рдмрдбрд╝реА рдкрд░рд┐рдпреЛрдЬрдирд╛рдУрдВ рдХреЗ рд▓рд┐рдП рдЕрдиреБрдХреВрд▓рди


рдмрдбрд╝реА рдкрд░рд┐рдпреЛрдЬрдирд╛рдУрдВ рдХреЗ рдорд╛рдорд▓реЗ рдореЗрдВ, рд╕рдВрдХреНрд░рдордг рдмрд╣реБрдд рд╕рдордп рд▓реЗ рд╕рдХрддрд╛ рд╣реИред рдпрджрд┐ рдЖрдк рдХреЗрд╡рд▓ рд╕рдВрд╢реЛрдзрд┐рдд рдлрд╝рд╛рдЗрд▓реЛрдВ рдХреЛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рддреЗ рд╣реИрдВ рддреЛ рдЖрдк CI рдХреЗ рджреМрд░рд╛рди рдирд┐рд╖реНрдкрд╛рджрди рдХреЛ рдЕрдиреБрдХреВрд▓рд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдЕрдзрд┐рдХ рдЬрд╛рдирдХрд╛рд░реА рдХреЗ рд▓рд┐рдП, рджрд╕реНрддрд╛рд╡реЗрдЬрд╝ рджреЗрдЦреЗрдВ: https://infection.imtqy.com/guide/how-to.html

рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдЖрдк рд╕рдорд╛рдирд╛рдВрддрд░ рдореЗрдВ рд╕рдВрд╢реЛрдзрд┐рдд рдХреЛрдб рдкрд░ рдкрд░реАрдХреНрд╖рдг рдЪрд▓рд╛ рд╕рдХрддреЗ рд╣реИрдВред рд╣рд╛рд▓рд╛рдВрдХрд┐, рдпрд╣ рдХреЗрд╡рд▓ рддрднреА рд╕рдВрднрд╡ рд╣реИ рдЬрдм рд╕рднреА рдкрд░реАрдХреНрд╖рдг рд╕реНрд╡рддрдВрддреНрд░ рд╣реЛрдВред рдЕрд░реНрдерд╛рддреН, рдРрд╕реЗ рдкрд░реАрдХреНрд╖рдг рдЕрдЪреНрдЫреЗ рд╣реЛрдиреЗ рдЪрд╛рд╣рд┐рдПред рдЗрд╕ рд╡рд┐рдХрд▓реНрдк рдХреЛ рд╕рдХреНрд╖рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП тАУthreads рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ:

 ./vendor/bin/infection --threads=4 

рдпрд╣ рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ?


Infection рдврд╛рдВрдЪреЗ рдореЗрдВ AST (Abstract Syntax Tree) рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЬреЛ рдПрдХ рд╕рд╛рд░ рдбреЗрдЯрд╛ рд╕рдВрд░рдЪрдирд╛ рдХреЗ рд░реВрдк рдореЗрдВ рдХреЛрдб рдХрд╛ рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ рдХрд░рддрд╛ рд╣реИред рдЗрд╕рдХреЗ рд▓рд┐рдП, PHP ( php-parser ) рдХреЗ рд░рдЪрдирд╛рдХрд╛рд░реЛрдВ рдореЗрдВ рд╕реЗ рдПрдХ рджреНрд╡рд╛рд░рд╛ рд▓рд┐рдЦреЗ рдЧрдП рдкрд╛рд░реНрд╕рд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред

рдЙрдкрдХрд░рдг рдХреЗ рд╕рд░рд▓реАрдХреГрдд рд╕рдВрдЪрд╛рд▓рди рдХреЛ рдирд┐рдореНрдирд╛рдиреБрд╕рд╛рд░ рджрд░реНрд╢рд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ:

  1. рдХреЛрдб рдЖрдзрд╛рд░рд┐рдд рдПрдПрд╕рдЯреА рдкреАрдврд╝реАред
  2. рдЙрдкрдпреБрдХреНрдд рдЙрддреНрдкрд░рд┐рд╡рд░реНрддрди (рдкреВрд░реНрдг рд╕реВрдЪреА рдпрд╣рд╛рдВ рд╣реИ ) рдХрд╛ рдЙрдкрдпреЛрдЧред
  3. рдПрдПрд╕рдЯреА рдЖрдзрд╛рд░рд┐рдд рд╕рдВрд╢реЛрдзрд┐рдд рдХреЛрдб рдмрдирд╛рдПрдБред
  4. рдкрд░рд┐рд╡рд░реНрддрд┐рдд рдХреЛрдб рдХреЗ рд╕рдВрдмрдВрдз рдореЗрдВ рдкрд░реАрдХреНрд╖рдг рдЪрд▓рд╛рдПрдВред

рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдЖрдк рдорд╛рдЗрдирд╕ рдкреНрд░рддрд┐рд╕реНрдерд╛рдкрди рдореНрдпреВрдЯреЗрдЯрд░ рдкреНрд▓рд╕ рдХреА рдЬрд╛рдВрдЪ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:

 <?php declare(strict_types=1); namespace Infection\Mutator\Arithmetic; use Infection\Mutator\Util\Mutator; use PhpParser\Node; use PhpParser\Node\Expr\Array_; /** * @internal */ final class Plus extends Mutator { /** * Replaces "+" with "-" * @param Node&Node\Expr\BinaryOp\Plus $node * @return Node\Expr\BinaryOp\Minus */ public function mutate(Node $node) { return new Node\Expr\BinaryOp\Minus($node->left, $node->right, $node->getAttributes()); } protected function mutatesNode(Node $node): bool { if (!($node instanceof Node\Expr\BinaryOp\Plus)) { return false; } if ($node->left instanceof Array_ || $node->right instanceof Array_) { return false; } return true; } } 

mutate() рд╡рд┐рдзрд┐ рдПрдХ рдирдпрд╛ рддрддреНрд╡ рдмрдирд╛рддреА рд╣реИ, рдЬрд┐рд╕реЗ рдПрдХ рдкреНрд▓рд╕ рджреНрд╡рд╛рд░рд╛ рдмрджрд▓ рджрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред Node рд╡рд░реНрдЧ php-parser рдкреИрдХреЗрдЬ рд╕реЗ рд▓рд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ; рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдПрдПрд╕рдЯреА рдкрд░рд┐рдЪрд╛рд▓рдиреЛрдВ рдХреЗ рд▓рд┐рдП рдФрд░ PHP рдХреЛрдб рдХреЛ рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рд╣рд╛рд▓рд╛рдБрдХрд┐, рдпрд╣ рдкрд░рд┐рд╡рд░реНрддрди рдХрд╣реАрдВ рднреА рд▓рд╛рдЧреВ рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП mutatesNode() рд╡рд┐рдзрд┐ рдореЗрдВ рдЕрддрд┐рд░рд┐рдХреНрдд рд╢рд░реНрддреЗрдВ рд╣реИрдВред рдпрджрд┐ рдкреНрд▓рд╕ рдХреЗ рдмрд╛рдИрдВ рдУрд░ рдпрд╛ рдорд╛рдЗрдирд╕ рдХреЗ рджрд╛рдИрдВ рдУрд░ рдПрдХ рд╕рд░рдгреА рд╣реИ, рддреЛ рдкрд░рд┐рд╡рд░реНрддрди рдЕрд╕реНрд╡реАрдХрд╛рд░реНрдп рд╣реИред рдЗрд╕ рдХреЛрдб рдХреЗ рдХрд╛рд░рдг рдЗрд╕ рд╕реНрдерд┐рддрд┐ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ:

 $tab = [0] + [1]; is correct, but the following one isn't correct. $tab = [0] - [1]; 

рдкрд░рд┐рдгрд╛рдо


рдЙрддреНрдкрд░рд┐рд╡рд░реНрддрди рдкрд░реАрдХреНрд╖рдг рдПрдХ рдЙрддреНрдХреГрд╖реНрдЯ рдЙрдкрдХрд░рдг рд╣реИ рдЬреЛ рд╕реАрдЖрдИ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЛ рдкреВрд░рдХ рдХрд░рддрд╛ рд╣реИ рдФрд░ рдЖрдкрдХреЛ рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреА рдЧреБрдгрд╡рддреНрддрд╛ рдХрд╛ рдореВрд▓реНрдпрд╛рдВрдХрди рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреА рд╣рд░реА рд╣рд╛рдЗрд▓рд╛рдЗрдЯрд┐рдВрдЧ рд╣рдореЗрдВ рд╡рд┐рд╢реНрд╡рд╛рд╕ рдирд╣реАрдВ рджрд┐рд▓рд╛рддреА рд╣реИ рдХрд┐ рд╕рдм рдХреБрдЫ рдЕрдЪреНрдЫреА рддрд░рд╣ рд╕реЗ рд▓рд┐рдЦрд╛ рдЧрдпрд╛ рд╣реИред рдЖрдк рдореНрдпреВрдЯреЗрд╢рдирд▓ рдкрд░реАрдХреНрд╖рдг - рдпрд╛ рдкрд░реАрдХреНрд╖рдг рдкрд░реАрдХреНрд╖рдг рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреА рд╕рдЯреАрдХрддрд╛ рдореЗрдВ рд╕реБрдзрд╛рд░ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ - рдЬреЛ рд╕рдорд╛рдзрд╛рди рдХреЗ рдкреНрд░рджрд░реНрд╢рди рдореЗрдВ рд╣рдорд╛рд░реЗ рдЖрддреНрдорд╡рд┐рд╢реНрд╡рд╛рд╕ рдХреЛ рдмрдврд╝рд╛рддрд╛ рд╣реИред рдмреЗрд╢рдХ, рдореИрдЯреНрд░рд┐рдХреНрд╕ рдХреЗ 100% рдкрд░рд┐рдгрд╛рдореЛрдВ рдХреЗ рд▓рд┐рдП рдкреНрд░рдпрд╛рд╕ рдХрд░рдирд╛ рдЖрд╡рд╢реНрдпрдХ рдирд╣реАрдВ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рд╣рдореЗрд╢рд╛ рд╕рдВрднрд╡ рдирд╣реАрдВ рд╣реЛрддрд╛ рд╣реИред рдЖрдкрдХреЛ рд▓реЙрдЧ рдХрд╛ рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдХрд░рдиреЗ рдФрд░ рддрджрдиреБрд╕рд╛рд░ рдкрд░реАрдХреНрд╖рдг рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред

Source: https://habr.com/ru/post/hi457888/


All Articles