See the Elephant

1992生まれのプログラマが書くエンジニアブログ

テスト駆動開発(TDD)はもう終わっているのか? Part1を読んでリポジトリ層に関するMockについて思いをはせる

postd.cc

TDDとモックの話

  • Mockによって中間地点のゴールを作ることができるメリットがある

  • Mockを多用しすぎることでリファクタリングが難しくなる場合もある

mock多用のチームからmock使わないチームに異動したけど、個人的にはない方が楽やなと思う

過去の和田さん(t-wada)とのペアプロ

「現実を模したMockを作り実装を合わせるべきで、実装に合わせたmockを作るべきでない 」

と言われたことを覚えている

実装のあとにテストを行うと実装に引きずられたテストケースとMockを書きがち

これを追認のテストと呼ぶそう。

Mockが便利すぎた結果、実装を"許す"形でテストを書かないように気をつけなければいけない、とおっしゃってた

Mockがないと、現実の制約の中で戦わざるをえないのでそっちのほうが結果的に楽だなと思う

友人がt-wadaさんのツイートを教えてくれたので雑談

DBドライバのMockを"自作自演"と呼ぶのはとても腑に落ちて素晴らしい表現だと思う。

友人曰く、

なんでもかんでもモックするのはどうなんだろう? という同じ疑問を持っていた

だそう。

自分的に以下のように考えている。

DB ドライバをモックにするとひたすら自作自演を行うテストになってしまうので、私は実際にデータベースとやりとりを行うテストを書きます。

あーもうほんとうにこれです。

前のプロジェクトはrepositoryをmockしてたけど、DBが返却しない値を設定できてしまうのでそれはあまり勧められた設計ではないなと思った

自分の開発マシンに入るものは常に本物を使い、入らないものだけモック/スタブを使います

めっちゃなるほどだ。。。

(友人) 今だとDocker とかで本番に近い環境整えることができるからなおさら本物を使ったほうがいいのかなーと思う

(自分) 今のチームだと実際にDB書き/読みにいくところまでテストされてますね

DBの制約とかDTOビジネスロジックのテストとしてはそっちの方が明らかに利点あるなーと思います

友人の言う通り、今ならdockerでチームメイトに同じDB seedや環境を配ることができる。

そう思うとDBに対してはMockingせず、本物を使う方がいいのだろう。

mysql server(mysqld)の起動をn回リトライして待つshellコマンドとmakefileを書いた

この記事の対象読者

  • mysql serverの起動を待つスクリプトが欲しい
  • docker-compose で mysqlを使っているが上手く初期化できない

    • なぜか初期化処理がbuildプロセスだけ失敗する
  • 以下のエラーでググってhostを直しても一向に直らない

ERROR 2013 (HY000): Lost connection to MySQL server at 'reading initial communication packet', system error: 0

TL;DR

mysql server(mysqld) の起動を待つ処理を入れましょう。
通常であればこれでいいと思います

$ mysqladmin --wait --count 3 ping || exit 1

docker-composeだとちょっと癖があるので上記以外で対処します。

shellはこんな感じ

for i in 1 2 3 4 5; do mysqladmin ping &>/dev/null && break || sleep 1  && [ 5 -eq $i ] && exit 1; done

人によってはこっちでもいいかも: 処理が成功するまで実行しつづける@katzchang

mysql server(mysqld)の起動を待つshellコマンドとmakefileを書いた

mysql server(mysqld)で起動直後にサーバ接続ができないケースがあった。

困ったので接続できるまでn回リトライし続けるshellコマンドと makefileを書いた。

無限に待ちたいわけではない ので今回は n回 と言う制約を持たせる。

僕はdocker-composeを使っているのでそれ前提で話をすすめる。

同じく困ってる人はいるんじゃないかな、と思ったのでメモる。

docker-compose で mysqlコンテナへ起動直後に接続できない!

今回は docker-composemysqlのコンテナを立てるケースを考える。

コンテナのbuild直後にmysqlに接続できないことがあった。

これでは、buildしたあとにエラーが起きDBの初期化処理が続けてできない。

マイグレーションもできないし、設定もできなくて困る。

コンテナ起動直後はmysql server(mysqld)が立ち上がっていない。

docker container が立ち上がった直後は mysql server(mysqld) がまだ起動していない。

mysql client(mysqladmin) から接続を試みるとこのエラーが出る。

ERROR 2013 (HY000): Lost connection to MySQL server at 'reading initial communication packet', system error: 0

エラーメッセージでぐぐるとhostの設定が足りないとの記事が出てきてめちゃめちゃハマった。

そうじゃない。

sleep n秒が1番簡単な解決方法

マイグレーションなどの処理の前に sleep 60 する

mysql server のデーモン起動(mysqld)まで待てばいいのでこれでok。

起動までリトライしたい

60秒を超えてmysql serverが起動した場合、 sleep 60 だと対応できない。 時間 or n回 リトライするような作りになっていればよい。

How do I write a retry logic in script to keep retrying to run it upto 5 times? より拝借

for i in 1 2 3 4 5; do command && break || sleep 1; done

これを使えば 5回, 1秒ごとに 成功するまでcmdを繰り返す 状態を作り出せる。

mysql serverのlifecheckは mysqladmin ping で行える。

for i in 1 2 3 4 5; do mysqladmin ping && break || sleep 1 ; done

これだと mysql serverが起動してようとしてなろうと次の処理へ行ってしまう。

そこで最終ループのときに exit 1 で抜けるようにした

for i in 1 2 3 4 5; do mysqladmin ping && break || sleep 1 && [ 5 -eq $i ] && exit 1; ; done

makefile で rangeを指定する

build中のタスクランナーとしてmakefileを使っている。

n回処理する記述は以下のように書ける。

RETRY_COUNT=30
RETRY_RANGE=$(shell seq 1 $(RETRY_COUNT) | xargs)

mysql/lifecheck: 
  for i in $(RETRY_RANGE); do mysqladmin ping &>/dev/null && break || echo '.' && sleep 1  && [ $(RETRY_COUNT) -eq $$i ] && exit 1; done

mysql/wait:
  which mysqladmin
   @echo "waiting boot mysql..."
  $(MAKE) mysql/lifecheck

dockerでmysqlコンテナを立てる時の設定 my.cnf

dockerでmysqlコンテナを立てる時の設定 my.cnf

mysqlのconf について学ぶ。

ざっと出すとこんな感じ

[mysqld]
skip-host-cache
skip-name-resolve
character-set-server=utf8mb4

[client]
default-character-set=utf8mb4

mysql server, clientの設定がそれぞれ書いてある。

server 設定 mysqld

skip-host-cache

www.na3.jp

MySQL :: MySQL 5.6 リファレンスマニュアル :: 8.11.5.2 DNS ルックアップの最適化とホストキャッシュ

これはDNSのキャッシュ戦略機能をオフにする宣言。

ipアドレスから引かれたホスト名がすでにキャッシュされている時にそれを使い回す機能があるそう。

docker-composeでコンテナを立てる場合、docker networkがよしなにprivate ipを当ててくれるからわざわざdnsで名前解決しなくていい。

skip-name-resolve

gihyo.jp

MySQL :: MySQL 5.6 リファレンスマニュアル :: 8.11.5.2 DNS ルックアップの最適化とホストキャッシュ

これも同様にDNSの名前引きを飛ばす設定。

character-set-server=utf8mb4

mb4 がポイント。

mysqlのutf8は1-3byte文字しか扱えない。

iOS, Androidの絵文字, 最近追加された難しい日本語や中国語は4byteあるためこれらの文字列を格納しようとするとエラーが起きる。

そこで utf8mb4を使う。

flhouse.co.jp

clientも同じ

クリーンアーキテクチャから学ぶSOLID : SRP

クリーンアーキテクチャから学ぶSOLID : SRP

クリーンアーキテクチャを読んでいる。

Clean Architecture 達人に学ぶソフトウェアの構造と設計

TL;DR : モジュールはたったひとつのアクターに対して責務を負うべきである が結論。

SRP: 単一責任の原則

要約すると アクターごとにモジュールを分けましょう。そうすると変更影響がアクター単位に閉じるから。 と言っている。

SRPはSingle Responsibility Principleの略で単一責任の原則と呼ばれる。

原著ではこう書かれている。

モジュールを変更する理由はたったひとつであるべきである

と書いてあるのだけど これが分かりにくい。

Unix哲学の 一つのことを上手くやる と混同しやすい表現になっている。

モジュールを変更する理由はたったひとつであるべきである = ひとつのことをうまくやる?

UNIXという考え方―その設計思想と哲学 によると

ひとつのことをうまくやるようにプログラムを作れないのであればおそらく問題をまだ完全には理解していないのだろう

自分は名前からはこう解釈した。

コードベースの変更理由が1つでない => 機能に持たせる役割が1つでない => 問題の切り分けが不完全 と誤解してしまう。

少なくともでもそうではないのだ。

SRPとは アクターごとにモジュールを分ける 提案をしている

SRPは 機能を小さくするべき ではなく、アクター(ユーザやプレースホルダー)ごとに機能を切り分けるべき と言っている。

クリーンアーキテクチャ では Facadeパターンを使って説明している。 TechScore Facadeパターン

Facadeパターンは機能の入り口となるクラスが他のクラスを利用することで機能を提供する。

他のクラスを使い方を知っており、一連の機能を提供するManager, Serviceクラスとその従属クラスのようなものを想像すればいい。

共通で使うクラスでアクターごとに違う振る舞いを他のクラスに移譲する

たとえ話をしよう。

3つの異なる役職のアクターがいるとする(例えばCTO, CFO, COO)。

こららアクターが共通で使うEmployeeクラスがある。

Employee には calculatePay 関数があり CFO, COOが利用している。

calculatePay をCOOの都合で書き換えた時、COOにも影響を与える。

CFOは知らないうちに数万ドルの損失を出すかもしれない。

複数のアクターで全く同じクラスを使うと、変更の影響が伝搬する。

<facadeを使うと…?>

このとき CFO, COOごとに (CfoCalculatePayのように) 機能を別のクラスに移譲する。

Employeeはアクターに適した振る舞いを持つ個々のクラスを呼び出す形にする。

そうすることで CFO都合の変更は CfoCalculatePay に閉じるし、 COO都合の変更は CooCalculatePayに閉じる。

モジュールを変更する理由はたったひとつであるべきである は主語が抜けていて誤解しやすい。 実際には モジュールはたったひとつのアクターに対して責務を負うべきである

クリーンアーキテクチャ では SRPをこのように解説している。

コンウェイの法則から導かれる当然の帰結。個々のモジュールを変更する理由がたったひとつだけになるように、ソフトウェアシステムの構造がそれを使う組織の社会的構造に大きな影響を受けるようにする

『Clean Architecture』における単一責任の原則とコンウェイの法則について

コンウェイの法則

システムを設計する組織は、組織のコミュニケーション構造をコピーした構造の設計を生み出す。

つまり、組織の構造をそのままアプリケーションの構造に取り入れましょう、と言う話なのだろう。

SRPについてそこそこ深く理解できた気がする。

フロントエンドについて学んだ2ヶ月

雑に文章を書きたかったので書いてみる。 fbもかえってきたし、自身の振り返りも兼ねて書いてみる。

気づけばエンジニアとして再びコードを書き始めてから2ヵ月ちょっとが経った。

7月にエンジニアに戻り、2ヵ月ほど引き継ぎをして、9月に正式にコードを書く人間になった。

9-11月はフロントもapiも書いていた。

どちらかといえばフロントのコードを書いていた時間が長いように思う。

その過程で得たフロントサイドの学びについて雑記してみる。

あと最近の気づきも。

理解したことをヴァッと書いてみる。

frontside覚えること多いですね

人材事業に入ってからreact(+redux)のコードを読み書きすることが多い。 先週今週の業務はほとんどreact+reduxのコードを読んでいた。

広告事業にいた時はphpでサーバーばっかり書いていた。主な担当は管理画面だったけど、フロントはサーバサイドレンダリングが多かったし、多少のvuejsを触った程度だった。

reactの導入に伴い勉強もしたけど、実際にコードを書くことはなかった。

だから、がっつりapiとclientに別れているコードを触るのは初めて。

プロダクト

今のプロダクトではUIで起きる状態管理に関して、プロダクト初期〜現在にかけて時間をかけて色んなアプローチを試している。

もちろんライブラリのupdateや新機能の追加による影響もある。

その結果、画面ごとに設計が異なっていたり、状態管理の単位が違っていたりする。

設計や状態管理の単位が変わるたびに脳内でコンテキストスイッチを切り替えるのは結構大変な作業である。

実際、componentや状態を変えるための経路が多くどのように動作しているのか頭のなかに置いておくのが難しい。

何度もなんども同じ箇所を読み返して理解していく。

難しい。今も難しい。

この記事を書く5分前まで眉間にシワを寄せていた。

学びが多いし、そもそも学ばないとコードが読めない。

そもそもの哲学を理解しないと無理

そもそも何をしたいのか、どんな思想のもと動いているのか理解しないと無理だ。

コードに頻発する action? reducer? store? state? なんだそれよくわからん。

2年エンジニアをしていて、まともにform 1つも作れなくてびっくりした。

reduxはfluxを基にしている。

fluxもreduxも状態管理を関心ごとにしている。

細かい部分はインターネッツに任せるとして、それらがやりたいことの本質は 「一方向の状態操作」 に尽きると思う。

それを知らないと何もできない。

ほとんどの状態変更が一方向の操作

「reduxはイベントドリブンの状態変更を一方向に限定したもの」と理解しているのだけどもきっとそれほど間違った説明でもないだろう。

一方向の状態変更が

初期状態を描画 -> 状態変化 ->  イベント(action) 生成 ->  actionを拾って新しい状態を生成(reducer) -> 描画

みたいなlife cycleを永遠繰り返す。

reduxが管理するstateだから redux state と呼ぶことにしよう。

パーツを切り分けて一つ一つが協調して動く

画面を構成するパーツはcomponentとして分けられる。 componentをいっぱい集めて一つの画面をつくる。

そもそもhtmlがelementの集合によってできているのだからそれと理屈は同じ。

ちょっとした違いはreactが仮想化したdomをレンダリングすることだ。 html elementをreactがラップしており、そいつらを固めたパーツにしておく。

そしてreactがパーツを実際のhtmlとしてレンダリングしていく。domdom。

componentが知りうるのは、dom構造とそのdomに入りうるデータ/型、そして起こしうるイベントを伝達するインタフェースである。

さながらグラスワインタワーに水を差し込むように、空のcomponentに命を吹き込むかのごとく、上からデータを引き渡していく。

propsとして渡されたデータを基にcomponentは自分が引き受けたdomのレンダリングやイベント発火、エラー表示を引き受ける。

パーツが協調して動く。

reduxとしてのstateとcomponentとしてのstate

基本的には一方向の状態変更が行われると書いた。

あれは嘘だ。いや嘘ではないんだけど、それ以外の状態変更も起きうる。

componentの中で状態を持ちたいケースがある。

componentの中で閉じていい状態(例えば入力制御用のフラグとか)はcomponent の中でもつstateで解決すればいい。

react hooksを使えば簡単にstateが持てるし指定した状態に関する副作用も検知できる。

僕の中での問題は その使い分けがいまいちまだわかっていない ことだ。

どこまでをredux stateに持つべきでどこからをcomponent stateに持てばいいのかまだわかっていない。

おおよその理解。

component stateでcomponentに閉じた短期的な状態を保持しておいて、actionをおこし、その値で持ってredux stateを置き換えに行く。

componentの初期状態となりうるstateはredux stateに持っておく。

そんな感じで使われるのが今の主流なのかな〜。これはチームメイトに聞く。

ここはもっとコードを読んで書いていくしかない。

型....

vgに入ってからずっとphpを書いてきたし、個人で何かを作る時はrubyを書いていた。 llばっかり書いているから型に関する耐性がないしどのように型を利用するのかいまいちまだ馴染みがない。

コードを書く以前の問題で型に関するプラクティスというか経験が足りなくてそこで詰まることも多い。

恥ずかしながらジェネリクス?なにそれ?からスタート。

使用言語がtype script, scalaと型に厳格だし、とても詰まる。

export type PiyoPiyoPayload = Partial<
    Pick<Fuga, Exclude<keyof Fuga, 'hoge'>> & {
        piyo: Partial<Piyo>;
    }
>;

こういうのを見たときにまだ身構える気持ちが抜けない。

人生、雰囲気でプログラムを書いてきてしまったのだなぁと能力不足を感じる。

大丈夫、上記のコードも30分ほど調べて読めるようになった。

大丈夫、人間は学ぶ。

何が言いたいかというと日々何かを学べることはいいってこと

ありがたいことに日々必ず新しい学びがある。フロントだけでなくてサーバも、もちろん仕事の進め方も。

だけど、何より大切なのは「慢心せずに学ぶこと」なのかなと。

正直、昨日の自分より1つでも強くなっていればいい。

楽器だって学びを与えてくれるのだろう

基本を丁寧に積み上げ、日々磨き続ける。言葉にすると陳腐だけど何事においても大切だよね。その姿勢は見習いたい。

ありがたいことに 同僚?上司に ドラムを褒めていただけた。

あんまりドラムを褒められることがない人生だったのでとても嬉しい。


楽器をやってきてよかったと思うことは 「クソ地道な基礎練習がお前を上手くする」 と学べたことだ。

クソ地道な基礎練習は誰も褒めてくれないし、誰もやれなんて言わない。

メトロノームに合わせて棒を振る

なんて地味な練習に最近は興奮すら覚えるので稀有な癖を持って生まれたな、と自身でも思う。

楽器はまさにそうだけど 地味さを面白がってやれない なら上手くならないし仕事も似たようなもの。

プログラミングも地道に知らないことを調べて潰していって、書いて間違えてを繰り返す作業だ。

楽器の練習してるのと実はそんなにやること変わらない。

プログラマやデザイナが楽器弾ける人多いのはそういう理由なのかも。

2ヶ月前よりドラムうまい自信があるし、2ヶ月前よりnextのコード書ける自信ある。

日々成長してるんだろう。

日々必ず前進できるようにやっていき :muscle:

ウガンダ人と電通の孫請けとして開発を頼まれそうな話

shuzonです。

※ この記事はほぼ全て音声入力で書かれています。

この記事はOysters Advent Calender 集え、牡蠣戦士!!!6日目の記事です。

ウガンダ人と電通の孫請けとして開発を頼まれそうな話

突然ですがウガンダ人と電通の孫請けとして rails で開発を行いそうです。

何でウガンダ人なのかについては今から話していこうと思います。

ウガンダへの道

高校の頃一緒に遊んでいた友達が月日を経てウガンダで働くことになったそうです。 彼は今本田圭佑の下でサッカークラブを経営しています。つまり代表です。

そんな奴が友達なんですが、海外で働いてみたいと話したところ「俺のルームシェアメイトがウガンダで it 会社をやっているからお前働いてみろよ」と行ってその人と繋いでくれました。

この前恵比寿でその人と話をしたんですがどうやら今はウガンダでオフショアの仕事をやっているそうです。

彼自身も Android エンジニアでかつウガンダで経営をやっているそうです。

話を聞いていく限り自社サービスと受注の二つの事業をやっているそうです。

railsと英語を使おう

僕は今回受注の仕事の中で rails のサーバーサイドエンジニアとして JOIN することになりました。

rails サーバサイドを書いた経験は学生の頃に一度だけあります。

たので少し不安ですがおそらくできるだろうと思っています。

どうやらやる内容としては Google音声認識 API を使い文字データをテキストに起こすという仕事をやるそうです。

まあ API 叩くぐらいだったら簡単だろうと受けてみたのですが…。

久しぶりのRailsだし、そもそもやり取りは全て英語なのでかなりチャレンジングです。

ひとりのエンジニアとして世界で働いてみよう

ちなみにウガンダ人エンジニアを一人雇うと月当たり5万円で済むそうです。

公用語は英語なのでもちろん彼らは英語を喋れます。

英語が話せる rails エンジニアが月5万円で雇われる時代なので日本で働いているエンジニアは日本語以外の言語を学ばない限りどうなるのでしょうか。

なんとなくその先には怖い未来が待っていると思います。

なぜか日本だけが日本語が喋れる民族だけで残っている唯一の経済国です。

他の国はほとんど英語が中国語が通じます。

何を意味しているかと言うと日本人が日本語しか喋れないから日本語で仕事が成り立つということです。

ローカライズに価値があるので仕事が成り立っているし大きいお金を持っている企業がまだ幾つか存在するので経済が成り立っているそうです。

ですが電通の孫請けがウガンダに仕事をする際は2月あたり大体5万円で済んでしまいます。

人月が大体80万円から200万円の日本人からすると魔王予想経済的効果としては何倍なんでしょうか。

このように数字で考えてみると海外のエンジニアと働くメリットのようなものが見えてくると思います。

国内にいながらにしてそういう挑戦的な環境を手に入れることができたのは嬉しい限りです。

生かすか殺すかは自分次第なのでいくらか頑張ってみようと思います。

Github ActionsでCI testを回す

Github ActionsでCI testを回す

このブログでやること

- github actionsでCI testを回す
- docker の nodeコンテナを使う
- CI testを行う

今回はすでに動いているリポジトリにci testを追加します。

nodeのバージョンを揃えるため、dockerコンテナを利用する方法を採用しました。

別の言語やruntimeを使っている人は適宜読み替えてください。

github actions がきた!

github actions きましたね。 github上で起こるアクション(push, prなど)に応じて特定のタスクを実行することができるタスクランナですね

f:id:namu_r21:20191117105905p:plain
github actions navi bar

リポジトリの 「actions」からいけます。 めちゃくちゃ設定が簡単で、30分ほどで設定できました。

GitHub Actionsについて - GitHub ヘルプ

詳しくはこちら

www.kaizenprogrammer.com

こちらの記事がusageについて詳しいです。

workflowが登録できる

「new workflow」をクリック

f:id:namu_r21:20191117110620p:plain
build workflow

ここで「set up a workflow」 します。
yamlファイルでworkflowを定義します。

yamlの項目はこちらに詳細に書いてあります。

GitHub Actionsのワークフロー構文 - GitHub ヘルプ

初期状態はこちら。

name: CI

on: [push] # pushで実行

jobs: # ジョブ一覧
  build: # ジョブ

    runs-on: ubuntu-latest  # ジョブを動かす環境を選ぶ(複数指定可能)

    steps:
    - uses: actions/checkout@v1  # github のリソースにアクセスするアクション
    - name: Run a one-line script  # workflow name
      run: echo Hello, world! # CLIで実行されるshell command
    - name: Run a multi-line script
      run: |
        echo Add other actions to build,
        echo test, and deploy your project.

nodeでtestを回す準備

この節は適宜読み飛ばしたり、読みかえてください。

CIの前に依存パッケージインストール, ビルド, テストの準備を行います。

ここに関しては元々用意してあったため使いまわしました。

僕は開発にMakefileを利用しています。

プログラムのビルド作業を自動化するツールですね。

よく使うコマンドをMakefileに記述することで開発速度を早めることができるためです。

特にCIなど予め定められた特定の処理を行う場合には非常に優秀なツールです。

qiita.com

今回はnpmで依存パッケージのインストールと、gulpによるビルド、テスティングライブラリavaによるtestを行います。

Makefile


.PHONY: install gulp

npm=$(shell which npm)
npx=$(shell which npx)

install:
    $(npm) install # package.jsonの依存lib install

gulp:
    $(npx) gulp # gulpでビルド

test:
    $(npm) test # package.jsonのtestを叩きます

package.json (一部抜粋)

{
  ...,
  "devDependencies": {
    ... ,
    "ava": "^1.3.1",
    "gulp": "^4.0.0",
    "gulp-cli": "^2.0.1",
    ... ,
  },
  "ava": {
    "compileEnhancements": false,
    "extensions": [
      "ts"
    ],
    "require": [
      "ts-node/register"
    ]
  },
  "scripts": {
    "test": "tsc && ava"
  },
  ...
}

Github ActionsでCI testを回す

さて、準備が整いました。yamlを書いていきます。

今回は docker の nodeコンテナを利用します。

github.com

setup-node が利用可能です。

name: CI

on: [push, pull_request] # prが出た時, pushした時にciをトリガする

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - uses: actions/setup-node@v1 # これがnode コンテナ
        with:
          node-version: '10.x' # withでバージョン指定
      - run: make install # makefileで指定したタスクをrun!
      - run: make gulp
      - run: make test

job はデフォルトで並列実行されます。

このため、 このjobでは install -> build -> test を1つの jobで行っています。

個々の役割を job に切り分けることも可能です。
その場合は jobs.<job_id>.needs で依存jobを指定できます。

一度試してみましたが、needsを書くだけではうまく動きませんでした。 jobごとにインスタンスが生成されるのでその辺を解消しないといけません。

今回はそこまでやらなくてもいいのでここまで。